USB: move PCI host controllers to new PM framework
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 27 Apr 2009 17:33:24 +0000 (13:33 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 16 Jun 2009 04:44:44 +0000 (21:44 -0700)
This patch (as1236) converts the USB PCI power management routines
over to the new PM framework.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.h
drivers/usb/host/ehci-pci.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/uhci-hcd.c

index a4301dc..5db4d40 100644 (file)
@@ -185,194 +185,198 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
 
-
-#ifdef CONFIG_PM
-
 /**
- * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
- * @dev: USB Host Controller being suspended
- * @message: Power Management message describing this state transition
- *
- * Store this function in the HCD's struct pci_driver as .suspend.
+ * usb_hcd_pci_shutdown - shutdown host controller
+ * @dev: USB Host Controller being shutdown
  */
-int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
+void usb_hcd_pci_shutdown(struct pci_dev *dev)
+{
+       struct usb_hcd          *hcd;
+
+       hcd = pci_get_drvdata(dev);
+       if (!hcd)
+               return;
+
+       if (hcd->driver->shutdown)
+               hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int check_root_hub_suspended(struct device *dev)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+
+       if (!(hcd->state == HC_STATE_SUSPENDED ||
+                       hcd->state == HC_STATE_HALT)) {
+               dev_warn(dev, "Root hub is not suspended\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+static int hcd_pci_suspend(struct device *dev)
 {
-       struct usb_hcd          *hcd = pci_get_drvdata(dev);
-       int                     retval = 0;
-       int                     wake, w;
-       int                     has_pci_pm;
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
 
        /* 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->state == HC_STATE_SUSPENDED ||
-                       hcd->state == HC_STATE_HALT)) {
-               dev_warn(&dev->dev, "Root hub is not suspended\n");
-               retval = -EBUSY;
-               goto done;
-       }
+       retval = check_root_hub_suspended(dev);
+       if (retval)
+               return retval;
 
        /* We might already be suspended (runtime PM -- not yet written) */
-       if (dev->current_state != PCI_D0)
-               goto done;
+       if (pci_dev->current_state != PCI_D0)
+               return retval;
 
        if (hcd->driver->pci_suspend) {
-               retval = hcd->driver->pci_suspend(hcd, message);
+               retval = hcd->driver->pci_suspend(hcd, PMSG_SUSPEND);
                suspend_report_result(hcd->driver->pci_suspend, retval);
                if (retval)
-                       goto done;
+                       return retval;
        }
 
-       synchronize_irq(dev->irq);
+       synchronize_irq(pci_dev->irq);
 
        /* 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.
+        * link (except maybe for PME# resume signaling).  We'll enter a
+        * low power state during suspend_noirq, if the hardware allows.
         */
-       pci_disable_device(dev);
+       pci_disable_device(pci_dev);
+       return retval;
+}
+
+static int hcd_pci_suspend_noirq(struct device *dev)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
+
+       retval = check_root_hub_suspended(dev);
+       if (retval)
+               return retval;
 
-       pci_save_state(dev);
+       pci_save_state(pci_dev);
 
-       /* Don't fail on error to enable wakeup.  We rely on pci code
-        * to reject requests the hardware can't implement, rather
-        * than coding the same thing.
+       /* If the root hub is HALTed rather than SUSPENDed,
+        * disallow remote wakeup.
         */
-       wake = (hcd->state == HC_STATE_SUSPENDED &&
-                       device_may_wakeup(&dev->dev));
-       w = pci_wake_from_d3(dev, wake);
-       if (w < 0)
-               wake = w;
-       dev_dbg(&dev->dev, "wakeup: %d\n", wake);
-
-       /* Don't change state if we don't need to */
-       if (message.event == PM_EVENT_FREEZE ||
-                       message.event == PM_EVENT_PRETHAW) {
-               dev_dbg(&dev->dev, "--> no state change\n");
-               goto done;
-       }
+       if (hcd->state == HC_STATE_HALT)
+               device_set_wakeup_enable(dev, 0);
+       dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev));
 
-       has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-       if (!has_pci_pm) {
-               dev_dbg(&dev->dev, "--> PCI D0 legacy\n");
+       /* Possibly enable remote wakeup,
+        * choose the appropriate low-power state, and go to that state.
+        */
+       retval = pci_prepare_to_sleep(pci_dev);
+       if (retval == -EIO) {           /* Low-power not supported */
+               dev_dbg(dev, "--> PCI D0 legacy\n");
+               retval = 0;
+       } else if (retval == 0) {
+               dev_dbg(dev, "--> PCI %s\n",
+                               pci_power_name(pci_dev->current_state));
        } else {
-
-               /* NOTE:  dev->current_state becomes nonzero only here, and
-                * only for devices that support PCI PM.  Also, exiting
-                * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset
-                * some device state (e.g. as part of clock reinit).
-                */
-               retval = pci_set_power_state(dev, PCI_D3hot);
-               suspend_report_result(pci_set_power_state, retval);
-               if (retval == 0) {
-                       dev_dbg(&dev->dev, "--> PCI D3\n");
-               } else {
-                       dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n",
-                                       retval);
-                       pci_restore_state(dev);
-               }
+               suspend_report_result(pci_prepare_to_sleep, retval);
+               return retval;
        }
 
 #ifdef CONFIG_PPC_PMAC
-       if (retval == 0) {
-               /* Disable ASIC clocks for USB */
-               if (machine_is(powermac)) {
-                       struct device_node      *of_node;
-
-                       of_node = pci_device_to_OF_node(dev);
-                       if (of_node)
-                               pmac_call_feature(PMAC_FTR_USB_ENABLE,
-                                                       of_node, 0, 0);
-               }
+       /* Disable ASIC clocks for USB */
+       if (machine_is(powermac)) {
+               struct device_node      *of_node;
+
+               of_node = pci_device_to_OF_node(pci_dev);
+               if (of_node)
+                       pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
        }
 #endif
-
- done:
        return retval;
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
 
-/**
- * usb_hcd_pci_resume - power management resume of a PCI-based HCD
- * @dev: USB Host Controller being resumed
- *
- * Store this function in the HCD's struct pci_driver as .resume.
- */
-int usb_hcd_pci_resume(struct pci_dev *dev)
+static int hcd_pci_resume_noirq(struct device *dev)
 {
-       struct usb_hcd          *hcd;
-       int                     retval;
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
 
 #ifdef CONFIG_PPC_PMAC
        /* Reenable ASIC clocks for USB */
        if (machine_is(powermac)) {
                struct device_node *of_node;
 
-               of_node = pci_device_to_OF_node(dev);
+               of_node = pci_device_to_OF_node(pci_dev);
                if (of_node)
                        pmac_call_feature(PMAC_FTR_USB_ENABLE,
                                                of_node, 0, 1);
        }
 #endif
 
-       pci_restore_state(dev);
+       /* Go back to D0 and disable remote wakeup */
+       pci_back_from_sleep(pci_dev);
+       return 0;
+}
+
+static int resume_common(struct device *dev, bool hibernated)
+{
+       struct pci_dev          *pci_dev = to_pci_dev(dev);
+       struct usb_hcd          *hcd = pci_get_drvdata(pci_dev);
+       int                     retval;
 
-       hcd = pci_get_drvdata(dev);
        if (hcd->state != HC_STATE_SUSPENDED) {
-               dev_dbg(hcd->self.controller,
-                               "can't resume, not suspended!\n");
+               dev_dbg(dev, "can't resume, not suspended!\n");
                return 0;
        }
 
-       pci_enable_wake(dev, PCI_D0, false);
-
-       retval = pci_enable_device(dev);
+       retval = pci_enable_device(pci_dev);
        if (retval < 0) {
-               dev_err(&dev->dev, "can't re-enable after resume, %d!\n",
-                               retval);
+               dev_err(dev, "can't re-enable after resume, %d!\n", retval);
                return retval;
        }
 
-       pci_set_master(dev);
-
-       /* yes, ignore this result too... */
-       (void) pci_wake_from_d3(dev, 0);
+       pci_set_master(pci_dev);
 
        clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
 
        if (hcd->driver->pci_resume) {
                retval = hcd->driver->pci_resume(hcd);
                if (retval) {
-                       dev_err(hcd->self.controller,
-                               "PCI post-resume error %d!\n", retval);
+                       dev_err(dev, "PCI post-resume error %d!\n", retval);
                        usb_hc_died(hcd);
                }
        }
        return retval;
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);
 
-#endif /* CONFIG_PM */
-
-/**
- * usb_hcd_pci_shutdown - shutdown host controller
- * @dev: USB Host Controller being shutdown
- */
-void usb_hcd_pci_shutdown(struct pci_dev *dev)
+static int hcd_pci_resume(struct device *dev)
 {
-       struct usb_hcd          *hcd;
-
-       hcd = pci_get_drvdata(dev);
-       if (!hcd)
-               return;
+       return resume_common(dev, false);
+}
 
-       if (hcd->driver->shutdown)
-               hcd->driver->shutdown(hcd);
+static int hcd_pci_restore(struct device *dev)
+{
+       return resume_common(dev, true);
 }
-EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
 
+struct dev_pm_ops usb_hcd_pci_pm_ops = {
+       .suspend        = hcd_pci_suspend,
+       .suspend_noirq  = hcd_pci_suspend_noirq,
+       .resume_noirq   = hcd_pci_resume_noirq,
+       .resume         = hcd_pci_resume,
+       .freeze         = check_root_hub_suspended,
+       .freeze_noirq   = check_root_hub_suspended,
+       .thaw_noirq     = NULL,
+       .thaw           = NULL,
+       .poweroff       = hcd_pci_suspend,
+       .poweroff_noirq = hcd_pci_suspend_noirq,
+       .restore_noirq  = hcd_pci_resume_noirq,
+       .restore        = hcd_pci_restore,
+};
+EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
+
+#endif /* CONFIG_PM_SLEEP */
index e7d4479..7f068d6 100644 (file)
@@ -261,14 +261,11 @@ struct pci_device_id;
 extern int usb_hcd_pci_probe(struct pci_dev *dev,
                                const struct pci_device_id *id);
 extern void usb_hcd_pci_remove(struct pci_dev *dev);
-
-#ifdef CONFIG_PM
-extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
-extern int usb_hcd_pci_resume(struct pci_dev *dev);
-#endif /* CONFIG_PM */
-
 extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
 
+#ifdef CONFIG_PM_SLEEP
+extern struct dev_pm_ops       usb_hcd_pci_pm_ops;
+#endif
 #endif /* CONFIG_PCI */
 
 /* pci-ish (pdev null is ok) buffer alloc/mapping support */
index 5aa8bce..8172383 100644 (file)
@@ -429,10 +429,11 @@ static struct pci_driver ehci_pci_driver = {
 
        .probe =        usb_hcd_pci_probe,
        .remove =       usb_hcd_pci_remove,
+       .shutdown =     usb_hcd_pci_shutdown,
 
-#ifdef CONFIG_PM
-       .suspend =      usb_hcd_pci_suspend,
-       .resume =       usb_hcd_pci_resume,
+#ifdef CONFIG_PM_SLEEP
+       .driver =       {
+               .pm =   &usb_hcd_pci_pm_ops
+       },
 #endif
-       .shutdown =     usb_hcd_pci_shutdown,
 };
index f9961b4..ee0a68c 100644 (file)
@@ -484,12 +484,11 @@ static struct pci_driver ohci_pci_driver = {
 
        .probe =        usb_hcd_pci_probe,
        .remove =       usb_hcd_pci_remove,
+       .shutdown =     usb_hcd_pci_shutdown,
 
-#ifdef CONFIG_PM
-       .suspend =      usb_hcd_pci_suspend,
-       .resume =       usb_hcd_pci_resume,
+#ifdef CONFIG_PM_SLEEP
+       .driver =       {
+               .pm =   &usb_hcd_pci_pm_ops
+       },
 #endif
-
-       .shutdown =     usb_hcd_pci_shutdown,
 };
-
index f2fd709..c013321 100644 (file)
@@ -940,10 +940,11 @@ static struct pci_driver uhci_pci_driver = {
        .remove =       usb_hcd_pci_remove,
        .shutdown =     uhci_shutdown,
 
-#ifdef CONFIG_PM
-       .suspend =      usb_hcd_pci_suspend,
-       .resume =       usb_hcd_pci_resume,
-#endif /* PM */
+#ifdef CONFIG_PM_SLEEP
+       .driver =       {
+               .pm =   &usb_hcd_pci_pm_ops
+       },
+#endif
 };
  
 static int __init uhci_hcd_init(void)