[PATCH] usbcore PCI glue updates for PM
authorDavid Brownell <david-b@pacbell.net>
Fri, 23 Sep 2005 05:38:16 +0000 (22:38 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 28 Oct 2005 23:47:40 +0000 (16:47 -0700)
This updates the PCI glue to address the new and simplified usbcore suspend
semantics, where CONFIG_USB_SUSPEND becomes irrelevant to HCDs because
hcd->hub_suspend() will always be called.

  - Removes now-unneeded recursion support

  - Go back to ignoring faults reported by the wakeup calls; we expect them
    to fail sometimes, and that's just fine.

The PCI HCDs will need simple changes to catch up to this, like being able
to ignore the setting of CONFIG_USB_SUSPEND.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
 drivers/usb/core/hcd-pci.c |  106 +++++++++++++++++++++------------------------
 drivers/usb/core/hcd.h     |    6 +-
 2 files changed, 53 insertions(+), 59 deletions(-)

drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.h

index 6385d1a..84d9e69 100644 (file)
@@ -30,6 +30,8 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <linux/usb.h>
+
+#include "usb.h"
 #include "hcd.h"
 
 
@@ -197,6 +199,26 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
 
        hcd = pci_get_drvdata(dev);
 
+       /* Root hub suspend should have stopped all downstream traffic,
+        * and all bus master traffic.  And done so for both the interface
+        * and the stub usb_device (which we check here).  But maybe it
+        * didn't; writing sysfs power/state files ignores such rules...
+        *
+        * We must ignore the FREEZE vs SUSPEND distinction here, because
+        * otherwise the swsusp will save (and restore) garbage state.
+        */
+       if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON)
+               return -EBUSY;
+
+       if (hcd->driver->suspend) {
+               retval = hcd->driver->suspend(hcd, message);
+               if (retval) {
+                       dev_dbg (&dev->dev, "PCI pre-suspend fail, %d\n",
+                               retval);
+                       goto done;
+               }
+       }
+
        /* FIXME until the generic PM interfaces change a lot more, this
         * can't use PCI D1 and D2 states.  For example, the confusion
         * between messages and states will need to vanish, and messages
@@ -215,31 +237,13 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
         */
        has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
 
-       switch (hcd->state) {
-
-       /* entry if root hub wasn't yet suspended ... from sysfs,
-        * without autosuspend, or if USB_SUSPEND isn't configured.
+       /* Downstream ports from this root hub should already be quiesced, so
+        * there will be no DMA activity.  Now we can shut down the upstream
+        * link (except maybe for PME# resume signaling) and enter some PCI
+        * low power state, if the hardware allows.
         */
-       case HC_STATE_RUNNING:
-               hcd->state = HC_STATE_QUIESCING;
-               retval = hcd->driver->suspend (hcd, message);
-               if (retval) {
-                       dev_dbg (hcd->self.controller, 
-                                       "suspend fail, retval %d\n",
-                                       retval);
-                       break;
-               }
-               hcd->state = HC_STATE_SUSPENDED;
-               /* FALLTHROUGH */
+       if (hcd->state == HC_STATE_SUSPENDED) {
 
-       /* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the
-        * controller and/or root hub will already have been suspended,
-        * but it won't be ready for a PCI resume call.
-        *
-        * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will
-        * have been called, otherwise root hub timers still run ...
-        */
-       case HC_STATE_SUSPENDED:
                /* no DMA or IRQs except when HC is active */
                if (dev->current_state == PCI_D0) {
                        pci_save_state (dev);
@@ -248,7 +252,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
 
                if (!has_pci_pm) {
                        dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n");
-                       break;
+                       goto done;
                }
 
                /* NOTE:  dev->current_state becomes nonzero only here, and
@@ -259,28 +263,29 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
                retval = pci_set_power_state (dev, PCI_D3hot);
                if (retval == 0) {
                        dev_dbg (hcd->self.controller, "--> PCI D3\n");
-                       retval = pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup);
-                       if (retval)
-                               break;
-                       retval = pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup);
-               } else if (retval < 0) {
+
+                       /* Ignore these return values.  We rely on pci code to
+                        * reject requests the hardware can't implement, rather
+                        * than coding the same thing.
+                        */
+                       (void) pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup);
+                       (void) pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup);
+               } else {
                        dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n",
                                        retval);
                        (void) usb_hcd_pci_resume (dev);
-                       break;
                }
-               break;
-       default:
+
+       } else {
                dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
                        hcd->state);
                WARN_ON(1);
                retval = -EINVAL;
-               break;
        }
 
-       /* update power_state **ONLY** to make sysfs happier */
+done:
        if (retval == 0)
-               dev->dev.power.power_state = message;
+               dev->dev.power.power_state = PMSG_SUSPEND;
        return retval;
 }
 EXPORT_SYMBOL (usb_hcd_pci_suspend);
@@ -336,20 +341,9 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
                                dev->current_state);
                }
 #endif
-               retval = pci_enable_wake (dev, dev->current_state, 0);
-               if (retval) {
-                       dev_err(hcd->self.controller,
-                               "can't enable_wake to %d, %d!\n",
-                               dev->current_state, retval);
-                       return retval;
-               }
-               retval = pci_enable_wake (dev, PCI_D3cold, 0);
-               if (retval) {
-                       dev_err(hcd->self.controller,
-                               "can't enable_wake to %d, %d!\n",
-                               PCI_D3cold, retval);
-                       return retval;
-               }
+               /* yes, ignore these results too... */
+               (void) pci_enable_wake (dev, dev->current_state, 0);
+               (void) pci_enable_wake (dev, PCI_D3cold, 0);
        } else {
                /* Same basic cases: clean (powered/not), dirty */
                dev_dbg(hcd->self.controller, "PCI legacy resume\n");
@@ -371,17 +365,17 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
 
        dev->dev.power.power_state = PMSG_ON;
 
-       hcd->state = HC_STATE_RESUMING;
        hcd->saw_irq = 0;
 
-       retval = hcd->driver->resume (hcd);
-       if (!HC_IS_RUNNING (hcd->state)) {
-               dev_dbg (hcd->self.controller, 
-                               "resume fail, retval %d\n", retval);
-               usb_hc_died (hcd);
+       if (hcd->driver->resume) {
+               retval = hcd->driver->resume(hcd);
+               if (retval) {
+                       dev_err (hcd->self.controller,
+                               "PCI post-resume error %d!\n", retval);
+                       usb_hc_died (hcd);
+               }
        }
 
-       retval = pci_enable_device(dev);
        return retval;
 }
 EXPORT_SYMBOL (usb_hcd_pci_resume);
index eb21f13..74757fc 100644 (file)
@@ -182,12 +182,12 @@ struct hc_driver {
        int     (*start) (struct usb_hcd *hcd);
 
        /* NOTE:  these suspend/resume calls relate to the HC as
-        * a whole, not just the root hub; they're for bus glue.
+        * a whole, not just the root hub; they're for PCI bus glue.
         */
-       /* called after all devices were suspended */
+       /* called after suspending the hub, before entering D3 etc */
        int     (*suspend) (struct usb_hcd *hcd, pm_message_t message);
 
-       /* called before any devices get resumed */
+       /* called after entering D0 (etc), before resuming the hub */
        int     (*resume) (struct usb_hcd *hcd);
 
        /* cleanly make HCD stop writing memory and doing I/O */