summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/core.c')
-rw-r--r--drivers/usb/dwc3/core.c115
1 files changed, 96 insertions, 19 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 7ee61a89520b..9eb085f359ce 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -108,22 +108,27 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
{
u32 reg;
+ int i;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- if (enable && !dwc->dis_u3_susphy_quirk)
- reg |= DWC3_GUSB3PIPECTL_SUSPHY;
- else
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i));
+ if (enable && !dwc->dis_u3_susphy_quirk)
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg);
+ }
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (enable && !dwc->dis_u2_susphy_quirk)
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- else
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ if (enable && !dwc->dis_u2_susphy_quirk)
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ }
}
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
@@ -559,9 +564,17 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
+ u32 reg;
if (!dwc->ev_buf)
return;
+ /*
+ * Exynos platforms may not be able to access event buffer if the
+ * controller failed to halt on dwc3_core_exit().
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ if (!(reg & DWC3_DSTS_DEVCTRLHLT))
+ return;
evt = dwc->ev_buf;
@@ -599,6 +612,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
}
+static void dwc3_config_soc_bus(struct dwc3 *dwc)
+{
+ if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
+ reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
+ }
+}
+
static int dwc3_core_ulpi_init(struct dwc3 *dwc)
{
int intf;
@@ -957,12 +982,16 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
static void dwc3_core_setup_global_control(struct dwc3 *dwc)
{
+ unsigned int power_opt;
+ unsigned int hw_mode;
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+ power_opt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
- switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
+ switch (power_opt) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
/**
* WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
@@ -995,6 +1024,20 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
break;
}
+ /*
+ * This is a workaround for STAR#4846132, which only affects
+ * DWC_usb31 version2.00a operating in host mode.
+ *
+ * There is a problem in DWC_usb31 version 2.00a operating
+ * in host mode that would cause a CSR read timeout When CSR
+ * read coincides with RAM Clock Gating Entry. By disable
+ * Clock Gating, sacrificing power consumption for normal
+ * operation.
+ */
+ if (power_opt != DWC3_GHWPARAMS1_EN_PWROPT_NO &&
+ hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && DWC3_VER_IS(DWC31, 200A))
+ reg |= DWC3_GCTL_DSBLCLKGTNG;
+
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
dev_info(dwc->dev, "Running with FPGA optimizations\n");
@@ -1320,6 +1363,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
+ dwc3_config_soc_bus(dwc);
+
ret = dwc3_phy_power_on(dwc);
if (ret)
goto err_exit_phy;
@@ -1342,6 +1387,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
/*
+ * STAR 9001285599: This issue affects DWC_usb3 version 3.20a
+ * only. If the PM TIMER ECM is enabled through GUCTL2[19], the
+ * link compliance test (TD7.21) may fail. If the ECN is not
+ * enabled (GUCTL2[19] = 0), the controller will use the old timer
+ * value (5us), which is still acceptable for the link compliance
+ * test. Therefore, do not enable PM TIMER ECM in 3.20a by
+ * setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0.
+ */
+ if (DWC3_VER_IS(DWC3, 320A)) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg &= ~DWC3_GUCTL2_LC_TIMER;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
+ /*
* When configured in HOST mode, after issuing U3/L2 exit controller
* fails to send proper CRC checksum in CRC5 feild. Because of this
* behaviour Transaction Error is generated, resulting in reset and
@@ -1558,6 +1618,27 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
}
+static void dwc3_get_software_properties(struct dwc3 *dwc)
+{
+ struct device *tmpdev;
+ u16 gsbuscfg0_reqinfo;
+ int ret;
+
+ dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
+
+ /*
+ * Iterate over all parent nodes for finding swnode properties
+ * and non-DT (non-ABI) properties.
+ */
+ for (tmpdev = dwc->dev; tmpdev; tmpdev = tmpdev->parent) {
+ ret = device_property_read_u16(tmpdev,
+ "snps,gsbuscfg0-reqinfo",
+ &gsbuscfg0_reqinfo);
+ if (!ret)
+ dwc->gsbuscfg0_reqinfo = gsbuscfg0_reqinfo;
+ }
+}
+
static void dwc3_get_properties(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
@@ -2072,6 +2153,8 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
+ dwc3_get_software_properties(dwc);
+
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) {
ret = PTR_ERR(dwc->reset);
@@ -2250,7 +2333,6 @@ assert_reset:
static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
{
- unsigned long flags;
u32 reg;
int i;
@@ -2293,9 +2375,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
break;
if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
synchronize_irq(dwc->irq_gadget);
}
@@ -2312,7 +2392,6 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
{
- unsigned long flags;
int ret;
u32 reg;
int i;
@@ -2366,9 +2445,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
dwc3_otg_host_init(dwc);
} else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
- spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_resume(dwc);
- spin_unlock_irqrestore(&dwc->lock, flags);
}
break;