From 945c9c53125dc1b404c232a37889f1e7b77bcc6c Mon Sep 17 00:00:00 2001 From: Ran Wang Date: Wed, 27 Sep 2017 15:03:18 +0800 Subject: 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 diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 89c399f..f4b8973 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -45,6 +45,8 @@ Optional properties: a free-running PHY clock. - snps,dis-del-phy-power-chg-quirk: when set core will change PHY power from P0 to P1/P2/P3 without delay. + - snps,disable_devinit_u1u2: when set, disable device-initiated U1/U2 + LPM request in USB device mode. - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal utmi_l1_suspend_n, false when asserts utmi_sleep_n - snps,hird-threshold: HIRD threshold 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: -- cgit v0.10.2