Commit 5f827ea3 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] usbcore PCI glue updates for PM

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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg 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(-)
parent f3f3253d
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <linux/usb.h> #include <linux/usb.h>
#include "usb.h"
#include "hcd.h" #include "hcd.h"
...@@ -197,6 +199,26 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) ...@@ -197,6 +199,26 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
hcd = pci_get_drvdata(dev); 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 /* FIXME until the generic PM interfaces change a lot more, this
* can't use PCI D1 and D2 states. For example, the confusion * can't use PCI D1 and D2 states. For example, the confusion
* between messages and states will need to vanish, and messages * 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) ...@@ -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); has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
switch (hcd->state) { /* Downstream ports from this root hub should already be quiesced, so
* there will be no DMA activity. Now we can shut down the upstream
/* entry if root hub wasn't yet suspended ... from sysfs, * link (except maybe for PME# resume signaling) and enter some PCI
* without autosuspend, or if USB_SUSPEND isn't configured. * low power state, if the hardware allows.
*/ */
case HC_STATE_RUNNING: if (hcd->state == HC_STATE_SUSPENDED) {
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 */
/* 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 */ /* no DMA or IRQs except when HC is active */
if (dev->current_state == PCI_D0) { if (dev->current_state == PCI_D0) {
pci_save_state (dev); pci_save_state (dev);
...@@ -248,7 +252,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) ...@@ -248,7 +252,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
if (!has_pci_pm) { if (!has_pci_pm) {
dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n");
break; goto done;
} }
/* NOTE: dev->current_state becomes nonzero only here, and /* 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) ...@@ -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); retval = pci_set_power_state (dev, PCI_D3hot);
if (retval == 0) { if (retval == 0) {
dev_dbg (hcd->self.controller, "--> PCI D3\n"); dev_dbg (hcd->self.controller, "--> PCI D3\n");
retval = pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup);
if (retval) /* Ignore these return values. We rely on pci code to
break; * reject requests the hardware can't implement, rather
retval = pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup); * than coding the same thing.
} else if (retval < 0) { */
(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", dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n",
retval); retval);
(void) usb_hcd_pci_resume (dev); (void) usb_hcd_pci_resume (dev);
break;
} }
break;
default: } else {
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
hcd->state); hcd->state);
WARN_ON(1); WARN_ON(1);
retval = -EINVAL; retval = -EINVAL;
break;
} }
/* update power_state **ONLY** to make sysfs happier */ done:
if (retval == 0) if (retval == 0)
dev->dev.power.power_state = message; dev->dev.power.power_state = PMSG_SUSPEND;
return retval; return retval;
} }
EXPORT_SYMBOL (usb_hcd_pci_suspend); EXPORT_SYMBOL (usb_hcd_pci_suspend);
...@@ -336,20 +341,9 @@ int usb_hcd_pci_resume (struct pci_dev *dev) ...@@ -336,20 +341,9 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
dev->current_state); dev->current_state);
} }
#endif #endif
retval = pci_enable_wake (dev, dev->current_state, 0); /* yes, ignore these results too... */
if (retval) { (void) pci_enable_wake (dev, dev->current_state, 0);
dev_err(hcd->self.controller, (void) pci_enable_wake (dev, PCI_D3cold, 0);
"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;
}
} else { } else {
/* Same basic cases: clean (powered/not), dirty */ /* Same basic cases: clean (powered/not), dirty */
dev_dbg(hcd->self.controller, "PCI legacy resume\n"); dev_dbg(hcd->self.controller, "PCI legacy resume\n");
...@@ -371,17 +365,17 @@ int usb_hcd_pci_resume (struct pci_dev *dev) ...@@ -371,17 +365,17 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
dev->dev.power.power_state = PMSG_ON; dev->dev.power.power_state = PMSG_ON;
hcd->state = HC_STATE_RESUMING;
hcd->saw_irq = 0; hcd->saw_irq = 0;
retval = hcd->driver->resume (hcd); if (hcd->driver->resume) {
if (!HC_IS_RUNNING (hcd->state)) { retval = hcd->driver->resume(hcd);
dev_dbg (hcd->self.controller, if (retval) {
"resume fail, retval %d\n", retval); dev_err (hcd->self.controller,
usb_hc_died (hcd); "PCI post-resume error %d!\n", retval);
usb_hc_died (hcd);
}
} }
retval = pci_enable_device(dev);
return retval; return retval;
} }
EXPORT_SYMBOL (usb_hcd_pci_resume); EXPORT_SYMBOL (usb_hcd_pci_resume);
......
...@@ -182,12 +182,12 @@ struct hc_driver { ...@@ -182,12 +182,12 @@ struct hc_driver {
int (*start) (struct usb_hcd *hcd); int (*start) (struct usb_hcd *hcd);
/* NOTE: these suspend/resume calls relate to the HC as /* 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); 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); int (*resume) (struct usb_hcd *hcd);
/* cleanly make HCD stop writing memory and doing I/O */ /* cleanly make HCD stop writing memory and doing I/O */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment