CHROMIUM: xhci: fix USB3 device undetected after resume
authorWilliam wu <wulf@rock-chips.com>
Mon, 21 Nov 2016 06:06:19 +0000 (14:06 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Thu, 20 Apr 2017 08:28:56 +0000 (16:28 +0800)
Some xHC controllers (e.g. Rockchip rk3399) integrated in
DWC3 IP, will be powered down in S3, and reinitialized after
resume.

However, if a USB3 device is plugged before system enter S3,
the device will be disconnected after resume because of xHC
lose power. And the device can't be detected again even if
we reinitialize xHC. In this case, CCS and CSC is '0' and
can't reflect the current state of the port, also the link
state stays in Rx.Detect.

So try to do warm reset on resume to reset USB3 device to
the default state, also reset a USB3 link, and re-exchange
link configuration information.

BUG=chrome-os-partner:58347
TEST=Plug an USB3 flash drive in rk3399 Kevin board Type-C
port, then set system enter S3. Wakeup system, check if USB3
device can be detected after resume.

Change-Id: I90975a48866569f2c2422a244afc618a3e427f57
Signed-off-by: William wu <wulf@rock-chips.com>
Reviewed-on: https://chromium-review.googlesource.com/412487
Commit-Ready: Guenter Roeck <groeck@chromium.org>
Tested-by: Guenter Roeck <groeck@chromium.org>
Tested-by: Inno Park <ih.yoo.park@samsung.com>
Reviewed-by: Guenter Roeck <groeck@chromium.org>
Signed-off-by: Meng Dongyang <daniel.meng@rock-chips.com>
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci.h
include/linux/usb/xhci_pdriver.h

index e9675e8f0e54640838ee5f53a3ae591c2193f452..52997fbfc07cd958c5d2495ae900f501de59ddff 100644 (file)
@@ -1420,6 +1420,23 @@ int xhci_bus_resume(struct usb_hcd *hcd)
                        xhci_dbg(xhci, "reset stuck port %d\n", port_index);
                        continue;
                }
+
+               /*
+                * Workaround for missing CCS and CSC on resume if controller
+                * is powered down in S3 with device plugged in.
+                */
+               if ((xhci->quirks & XHCI_WARM_RESET_ON_RESUME) &&
+                   (hcd->speed >= HCD_USB3) &&
+                   !(temp & (PORT_CSC | PORT_CONNECT))) {
+                       /* clear wakeup/change bits, and do a warm port reset */
+                       temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
+                       temp |= PORT_WR;
+                       writel(temp, port_array[port_index]);
+                       /* flush write */
+                       readl(port_array[port_index]);
+                       continue;
+               }
+
                if (DEV_SUPERSPEED_ANY(temp))
                        temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
                else
index cf809492d3893d79381982cb560bba47bdbc8a91..2418740b6fac9be06e54813ba43afaa4374483b4 100644 (file)
@@ -54,6 +54,9 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
         */
        if (pdata && pdata->xhci_slow_suspend)
                xhci->quirks |= XHCI_SLOW_SUSPEND;
+
+       if (pdata && pdata->usb3_warm_reset_on_resume)
+               xhci->quirks |= XHCI_WARM_RESET_ON_RESUME;
 }
 
 /* called during probe() after chip reset completes */
index 7c413e2376b8bf1d82623d84a2618d64fac5c7b0..068b6571c64b8bf47b31d548f26408e7e1e1b10d 100644 (file)
@@ -1636,6 +1636,7 @@ struct xhci_hcd {
 #define XHCI_PME_STUCK_QUIRK   (1 << 20)
 #define XHCI_DIS_AUTOSUSPEND   (1 << 21)
 #define XHCI_MISSING_CAS       (1 << 24)
+#define XHCI_WARM_RESET_ON_RESUME      (1 << 25)
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
        /* There are two roothubs to keep track of bus suspend info for */
index cd03e02f5c9a3f088820e9d11f47686926d2ec04..5df42af8a404109feef78172543c7c1a01875247 100644 (file)
  *                     after the Run/Stop (R/S) bit is cleared to '0'.
  * @usb3_disable_autosuspend: determines if this xhci platform supports
  *                     USB3 autosuspend capability
+ * @usb3_warm_reset_on_resume: determines if it need warm reset on resume.
  *
  */
 struct usb_xhci_pdata {
        unsigned        usb3_lpm_capable:1;
        unsigned        xhci_slow_suspend:1;
        unsigned        usb3_disable_autosuspend:1;
+       unsigned        usb3_warm_reset_on_resume:1;
 };
 
 #endif /* __USB_CORE_XHCI_PDRIVER_H */