USB: ehci-platform: Support ehci reset after resume quirk
authorWu Liang feng <wulf@rock-chips.com>
Wed, 24 Dec 2014 10:22:19 +0000 (18:22 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 9 Jan 2015 20:25:54 +0000 (12:25 -0800)
The Rockchip rk3288 EHCI controller doesn't properly detect
the case when a device is removed during suspend. Specifically,
when usb resume from suspend, the EHCI controller maintaining
the USB state (FLAG_CF is 1, Current Connect Status is 1),
but a USB device (like a USB camera on rk3288) may have been
disconnected actually.

Let's add a quirk to force ehci to go into the
usb_root_hub_lost_power() path and reset after resume.
This should generally reset the whole controller and all
ports and initialize everything cleanly again, and bring
the devices back up.

As part of this, rename the "hibernation" paramter of
ehci_resume() to force_reset since hibernation is simply
another case where we can't trust the autodetected status
and need to force a reset of devices.

Signed-off-by: Wu Liang feng <wulf@rock-chips.com>
Reviewed-by: Julius Werner <jwerner@google.com>
Reviewed-by: Doug Anderson <dianders@google.com>
Reviewed-by: Tomasz Figa <tfiga@google.com>
Reviewed-by: Pawel Osciak <posciak@google.com>
Reviewed-by: Sonny Rao <sonnyrao@google.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Tested-by: Doug Anderson <dianders@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-platform.c
drivers/usb/host/ehci.h
include/linux/usb/ehci_pdriver.h

index 38bfeedae1d0f30cf990d6d5b2b4e450a5ca22e2..85e56d1abd2307a8620c6fcfa1211a4136f74faf 100644 (file)
@@ -1110,7 +1110,7 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 EXPORT_SYMBOL_GPL(ehci_suspend);
 
 /* Returns 0 if power was preserved, 1 if power was lost */
-int ehci_resume(struct usb_hcd *hcd, bool hibernated)
+int ehci_resume(struct usb_hcd *hcd, bool force_reset)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
 
@@ -1124,12 +1124,12 @@ int ehci_resume(struct usb_hcd *hcd, bool hibernated)
                return 0;               /* Controller is dead */
 
        /*
-        * If CF is still set and we aren't resuming from hibernation
+        * If CF is still set and reset isn't forced
         * then we maintained suspend power.
         * Just undo the effect of ehci_suspend().
         */
        if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
-                       !hibernated) {
+                       !force_reset) {
                int     mask = INTR_MASK;
 
                ehci_prepare_ports_for_controller_resume(ehci);
index 8557803e6154b0f64c5843af4f636cd1e66febf6..db5c29edf6dbaa067b73943b9b179925cf5fd9c8 100644 (file)
@@ -185,6 +185,10 @@ static int ehci_platform_probe(struct platform_device *dev)
                if (of_property_read_bool(dev->dev.of_node, "big-endian"))
                        ehci->big_endian_mmio = ehci->big_endian_desc = 1;
 
+               if (of_property_read_bool(dev->dev.of_node,
+                                         "needs-reset-on-resume"))
+                       pdata->reset_on_resume = 1;
+
                priv->phy = devm_phy_get(&dev->dev, "usb");
                if (IS_ERR(priv->phy)) {
                        err = PTR_ERR(priv->phy);
@@ -340,7 +344,7 @@ static int ehci_platform_resume(struct device *dev)
                        return err;
        }
 
-       ehci_resume(hcd, false);
+       ehci_resume(hcd, pdata->reset_on_resume);
        return 0;
 }
 #endif /* CONFIG_PM_SLEEP */
index 6f0577b0a5ae37de4acb99ec661553a78128a1b9..52ef0844a1802a77893ded8be690a960b9bfceb8 100644 (file)
@@ -871,7 +871,7 @@ extern int  ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr,
 
 #ifdef CONFIG_PM
 extern int     ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
-extern int     ehci_resume(struct usb_hcd *hcd, bool hibernated);
+extern int     ehci_resume(struct usb_hcd *hcd, bool force_reset);
 #endif /* CONFIG_PM */
 
 extern int     ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
index 7eb4dcd0d38663ab434919bd4d4e867788a4a6a0..6287b398abd9f9851abfe5d41b5f935f9f90d1aa 100644 (file)
@@ -34,6 +34,8 @@ struct usb_hcd;
  *                     after initialization.
  * @no_io_watchdog:    set to 1 if the controller does not need the I/O
  *                     watchdog to run.
+ * @reset_on_resume:   set to 1 if the controller needs to be reset after
+ *                     a suspend / resume cycle (but can't detect that itself).
  *
  * These are general configuration options for the EHCI controller. All of
  * these options are activating more or less workarounds for some hardware.
@@ -45,6 +47,7 @@ struct usb_ehci_pdata {
        unsigned        big_endian_desc:1;
        unsigned        big_endian_mmio:1;
        unsigned        no_io_watchdog:1;
+       unsigned        reset_on_resume:1;
 
        /* Turn on all power and clocks */
        int (*power_on)(struct platform_device *pdev);