summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
authorRan Wang <ran.wang_1@nxp.com>2017-09-27 07:03:18 (GMT)
committerXie Xiaobo <xiaobo.xie@nxp.com>2017-12-12 07:32:32 (GMT)
commit945c9c53125dc1b404c232a37889f1e7b77bcc6c (patch)
tree69c323167147e6393d9c3b3d1c36e438dcbbfbba /drivers/usb/dwc3
parent0a7b5391ea4efbebd64504ad5108e554e988c4b6 (diff)
downloadlinux-945c9c53125dc1b404c232a37889f1e7b77bcc6c.tar.xz
usb: dwc3: workaround: disable device-initiated U1/U2
Issue: When the USB controller is configured as a USB device mode, the device initiates low power when an ACK is pending for a data packet (DP). When operating in SuperSpeed mode and when the internal condition for low power (u1/u2) is satisfied, the device initiates u1/u2 even though it has just received a DPH of the DP header (DPH). This causes the link to enter and exit low power before the device sends an ACK for the DP. This behavior can cause a transaction timeout on the host for the DP. Impact: Depending on the host transaction timeout value, the host may timeout on the transaction and the host retries the transfer. If the same issue happens again, this could result in the host resetting the device and re-enumerating. Workaround: Disable USB_DCTL (InitU1Ena, InitU2Ena) bits. As a result,the device does not initiate lowpower requests; however, it can still accept low-power requests from the host/hub and enter low power. Signed-off-by: Ran Wang <ran.wang_1@nxp.com>
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/core.c2
-rw-r--r--drivers/usb/dwc3/core.h2
-rw-r--r--drivers/usb/dwc3/ep0.c4
-rw-r--r--drivers/usb/dwc3/gadget.c7
4 files changed, 13 insertions, 2 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index e34ef90..885d8e5 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1115,6 +1115,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
+ dwc->disable_devinit_u1u2_quirk = device_property_read_bool(dev,
+ "snps,disable_devinit_u1u2");
device_property_read_u8(dev, "snps,tx_de_emphasis",
&tx_de_emphasis);
device_property_read_string(dev, "snps,hsphy_interface",
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 9151eef..2549339 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -860,6 +860,7 @@ struct dwc3_scratchpad_array {
* 1 - -3.5dB de-emphasis
* 2 - No de-emphasis
* 3 - Reserved
+ * @disable_devinit_u1u2_quirk: disable device-initiated U1/U2 request.
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@@ -1016,6 +1017,7 @@ struct dwc3 {
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
+ unsigned disable_devinit_u1u2_quirk:1;
u16 imod_interval;
};
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 2331469..4e6c1bb 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -360,9 +360,9 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
- if (reg & DWC3_DCTL_INITU1ENA)
+ if ((reg & DWC3_DCTL_INITU1ENA) && !dwc->disable_devinit_u1u2_quirk)
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
- if (reg & DWC3_DCTL_INITU2ENA)
+ if ((reg & DWC3_DCTL_INITU2ENA) && !dwc->disable_devinit_u1u2_quirk)
usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a7e2809..56ffaa3 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2897,6 +2897,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt)
int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret, irq;
+ u32 reg;
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
@@ -3011,6 +3012,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err5;
}
+ if (dwc->disable_devinit_u1u2_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ }
+
return 0;
err5: