Merge remote-tracking branch 'origin/usb/dwc3' into usb/next

* origin/usb/dwc3: (10 commits)
  usb: dwc3: Add cache type configuration support
  usb: dwc3: Add chip-specific compatible string
  MLK-22675 usb: dwc3: host: disable park mode
  usb: dwc3: Add workaround for host mode VBUS glitch when boot
  usb: dwc3: Add avoiding vbus glitch happen during xhci reset
  ...
This commit is contained in:
Dong Aisheng 2019-12-02 18:02:01 +08:00
commit 7866db2c15
6 changed files with 242 additions and 9 deletions

View File

@ -4,7 +4,15 @@ DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties
as described in 'usb/generic.txt'
Required properties:
- compatible: must be "snps,dwc3"
- compatible: must be "snps,dwc3" and (if applicable) may contain a
chip-specific compatible string in front of it to allow dwc3 driver be
able to update cache type configuration accordingly, otherwise
Layerscape SoC will encounter USB init failure when adding property
dma-coherent on device tree.
Example:
* "fsl,layerscape-dwc3", "snps,dwc3"
* "snps,dwc3"
- reg : Address and length of the register set for the device
- interrupts: Interrupts used by the dwc3 controller.
- clock-names: should contain "ref", "bus_early", "suspend"
@ -109,6 +117,10 @@ Optional properties:
more than one value, which means undefined length INCR burst type
enabled. The values can be 1, 4, 8, 16, 32, 64, 128 and 256.
- snps,host-vbus-glitches: Power off all Root Hub ports immediately after
setting host mode to avoid vbus (negative) glitch happen in later
xhci reset. And the vbus will back to 5V automatically when reset done.
- in addition all properties from usb-xhci.txt from the current directory are
supported as well

View File

@ -894,6 +894,73 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
}
static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
{
u32 reg, scale;
if (dwc->num_clks == 0)
return;
/*
* The power down scale field specifies how many suspend_clk
* periods fit into a 16KHz clock period. When performing
* the division, round up the remainder.
*/
scale = DIV_ROUND_UP(clk_get_rate(dwc->clks[2].clk), 16384);
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
reg |= DWC3_GCTL_PWRDNSCALE(scale);
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
#ifdef CONFIG_OF
struct dwc3_cache_type {
u8 transfer_type_datard;
u8 transfer_type_descrd;
u8 transfer_type_datawr;
u8 transfer_type_descwr;
};
static const struct dwc3_cache_type layerscape_dwc3_cache_type = {
.transfer_type_datard = 2,
.transfer_type_descrd = 2,
.transfer_type_datawr = 2,
.transfer_type_descwr = 2,
};
/**
* dwc3_set_cache_type - Configure cache type registers
* @dwc: Pointer to our controller context structure
*/
static void dwc3_set_cache_type(struct dwc3 *dwc)
{
u32 tmp, reg;
const struct dwc3_cache_type *cache_type =
device_get_match_data(dwc->dev);
if (cache_type) {
reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
tmp = reg;
reg &= ~DWC3_GSBUSCFG0_DATARD(~0);
reg |= DWC3_GSBUSCFG0_DATARD(cache_type->transfer_type_datard);
reg &= ~DWC3_GSBUSCFG0_DESCRD(~0);
reg |= DWC3_GSBUSCFG0_DESCRD(cache_type->transfer_type_descrd);
reg &= ~DWC3_GSBUSCFG0_DATAWR(~0);
reg |= DWC3_GSBUSCFG0_DATAWR(cache_type->transfer_type_datawr);
reg &= ~DWC3_GSBUSCFG0_DESCWR(~0);
reg |= DWC3_GSBUSCFG0_DESCWR(cache_type->transfer_type_descwr);
if (tmp != reg)
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
}
}
#endif
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@ -918,6 +985,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->maximum_speed = USB_SPEED_HIGH;
}
dwc3_set_power_down_clk_scale(dwc);
ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
@ -952,6 +1021,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
#ifdef CONFIG_OF
dwc3_set_cache_type(dwc);
#endif
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
@ -1009,6 +1082,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GUCTL_HSTINAUTORETRY;
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
/*
* Disable Park Mode for super speed:
* Park mode is used in host mode when only a single async
* endpoint is active, but which has a known issue cause
* USB3.0 HC may die when read and write at the same time,
* considering the advantages of this mode are minimal,
* this issue only impacts super speed and exist on all IP
* versions, disable it for SS, Synopsys will release a formal
* STAR 9001415732, and disable it by default in next IP
* release.
*/
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
/*
@ -1236,6 +1324,17 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
if (dwc->dr_mode == USB_DR_MODE_OTG) {
dwc->otg_caps.otg_rev = 0x0300;
dwc->otg_caps.hnp_support = true;
dwc->otg_caps.srp_support = true;
dwc->otg_caps.adp_support = true;
/* Update otg capabilities by DT properties */
of_usb_update_otg_caps(dev->of_node,
&dwc->otg_caps);
}
dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
dwc->sysdev_is_parent = device_property_read_bool(dev,
@ -1315,6 +1414,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk");
dwc->host_vbus_glitches = device_property_read_bool(dev,
"snps,host-vbus-glitches");
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@ -1837,12 +1939,9 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = {
#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {
{
.compatible = "snps,dwc3"
},
{
.compatible = "synopsys,dwc3"
},
{ .compatible = "fsl,layerscape-dwc3", .data = &layerscape_dwc3_cache_type, },
{ .compatible = "snps,dwc3" },
{ .compatible = "synopsys,dwc3" },
{ },
};
MODULE_DEVICE_TABLE(of, of_dwc3_match);

View File

@ -25,6 +25,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h>
@ -165,6 +166,21 @@
/* Bit fields */
/* Global SoC Bus Configuration INCRx Register 0 */
#ifdef CONFIG_OF
#define DWC3_GSBUSCFG0_DATARD_SHIFT 28
#define DWC3_GSBUSCFG0_DATARD(n) (((n) & 0xf) \
<< DWC3_GSBUSCFG0_DATARD_SHIFT)
#define DWC3_GSBUSCFG0_DESCRD_SHIFT 24
#define DWC3_GSBUSCFG0_DESCRD(n) (((n) & 0xf) \
<< DWC3_GSBUSCFG0_DESCRD_SHIFT)
#define DWC3_GSBUSCFG0_DATAWR_SHIFT 20
#define DWC3_GSBUSCFG0_DATAWR(n) (((n) & 0xf) \
<< DWC3_GSBUSCFG0_DATAWR_SHIFT)
#define DWC3_GSBUSCFG0_DESCWR_SHIFT 16
#define DWC3_GSBUSCFG0_DESCWR(n) (((n) & 0xf) \
<< DWC3_GSBUSCFG0_DESCWR_SHIFT)
#endif
#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */
@ -223,6 +239,7 @@
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
#define DWC3_GCTL_PWRDNSCALE_MASK DWC3_GCTL_PWRDNSCALE(0x1fff)
#define DWC3_GCTL_U2RSTECN BIT(16)
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
#define DWC3_GCTL_CLK_BUS (0)
@ -251,6 +268,7 @@
/* Global User Control 1 Register */
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
/* Global Status Register */
#define DWC3_GSTS_OTG_IP BIT(10)
@ -941,6 +959,7 @@ struct dwc3_scratchpad_array {
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @otg_caps: the OTG capabilities from hardware point
* @revision: revision register contents
* @version_type: VERSIONTYPE register contents, a sub release of a revision
* @dr_mode: requested mode of operation
@ -1029,6 +1048,8 @@ struct dwc3_scratchpad_array {
* 2 - No de-emphasis
* 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
* @host_vbus_glitches: set to avoid vbus glitch during
* xhci reset.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
*/
@ -1078,6 +1099,7 @@ struct dwc3 {
void __iomem *regs;
size_t regs_size;
struct usb_role_switch *role_switch;
enum usb_dr_mode dr_mode;
u32 current_dr_role;
u32 desired_dr_role;
@ -1094,6 +1116,7 @@ struct dwc3 {
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
struct usb_otg_caps otg_caps;
/*
* All 3.1 IP version constants are greater than the 3.0 IP
@ -1218,6 +1241,7 @@ struct dwc3 {
unsigned tx_de_emphasis:2;
unsigned dis_metastability_quirk:1;
unsigned host_vbus_glitches:1;
u16 imod_interval;
};

View File

@ -476,6 +476,43 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
return edev;
}
static int dwc3_usb_role_switch_set(struct device *dev, enum usb_role role)
{
u32 mode;
switch (role) {
case USB_ROLE_HOST:
mode = DWC3_GCTL_PRTCAP_HOST;
break;
case USB_ROLE_DEVICE:
mode = DWC3_GCTL_PRTCAP_DEVICE;
break;
default:
return 0;
};
dwc3_set_mode(dev_get_drvdata(dev), mode);
return 0;
}
static enum usb_role dwc3_usb_role_switch_get(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
unsigned long flags;
enum usb_role role;
spin_lock_irqsave(&dwc->lock, flags);
role = dwc->current_dr_role;
spin_unlock_irqrestore(&dwc->lock, flags);
return role;
}
static struct usb_role_switch_desc dwc3_role_switch = {
.set = dwc3_usb_role_switch_set,
.get = dwc3_usb_role_switch_get,
};
int dwc3_drd_init(struct dwc3 *dwc)
{
int ret, irq;
@ -484,7 +521,13 @@ int dwc3_drd_init(struct dwc3 *dwc)
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
if (dwc->edev) {
if (device_property_read_bool(dwc->dev, "usb-role-switch")) {
dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
dwc->role_switch = usb_role_switch_register(dwc->dev,
&dwc3_role_switch);
if (IS_ERR(dwc->role_switch))
return PTR_ERR(dwc->role_switch);
} else if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
@ -531,6 +574,9 @@ void dwc3_drd_exit(struct dwc3 *dwc)
{
unsigned long flags;
if (dwc->role_switch)
usb_role_switch_unregister(dwc->role_switch);
if (dwc->edev)
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);

View File

@ -270,7 +270,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
u32 timeout = 1000;
u32 timeout = 2000;
u32 saved_config = 0;
u32 reg;
@ -3343,6 +3343,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
dwc->gadget.lpm_capable = true;
dwc->gadget.is_otg = (dwc->dr_mode == USB_DR_MODE_OTG) &&
(dwc->otg_caps.hnp_support ||
dwc->otg_caps.srp_support ||
dwc->otg_caps.adp_support);
/*
* FIXME We might be setting max_speed to <SUPER, however versions

View File

@ -9,8 +9,49 @@
#include <linux/platform_device.h>
#include "../host/xhci.h"
#include "core.h"
#define XHCI_HCSPARAMS1 0x4
#define XHCI_PORTSC_BASE 0x400
/*
* dwc3_power_off_all_roothub_ports - Power off all Root hub ports
* @dwc3: Pointer to our controller context structure
*/
static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
{
int i, port_num;
u32 reg, op_regs_base, offset;
void __iomem *xhci_regs;
/* xhci regs is not mapped yet, do it temperary here */
if (dwc->xhci_resources[0].start) {
xhci_regs = ioremap(dwc->xhci_resources[0].start,
DWC3_XHCI_REGS_END);
if (IS_ERR(xhci_regs)) {
dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
return;
}
op_regs_base = HC_LENGTH(readl(xhci_regs));
reg = readl(xhci_regs + XHCI_HCSPARAMS1);
port_num = HCS_MAX_PORTS(reg);
for (i = 1; i <= port_num; i++) {
offset = op_regs_base + XHCI_PORTSC_BASE + 0x10*(i-1);
reg = readl(xhci_regs + offset);
reg &= ~PORT_POWER;
writel(reg, xhci_regs + offset);
}
iounmap(xhci_regs);
} else
dev_err(dwc->dev, "xhci base reg invalid\n");
}
static int dwc3_host_get_irq(struct dwc3 *dwc)
{
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
@ -50,6 +91,13 @@ int dwc3_host_init(struct dwc3 *dwc)
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
int prop_idx = 0;
/*
* We have to power off all Root hub ports immediately after DWC3 set
* to host mode to avoid VBUS glitch happen when xhci get reset later.
*/
if (dwc->host_vbus_glitches)
dwc3_power_off_all_roothub_ports(dwc);
irq = dwc3_host_get_irq(dwc);
if (irq < 0)
return irq;