summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2004-05-11 01:41:37 -0700
committerGreg Kroah-Hartman <greg@kroah.com>2004-05-11 01:41:37 -0700
commit4808f141a8b359ea5cc29064cd2c1d4d44a8e159 (patch)
tree5a423d9cb636adebfeec268493a15806724c1814
parent3a6e35adc3a34f07117726e045a68f58b7904c8f (diff)
[PATCH] USB: more functional HCD PCI PM glue
This patch makes the usbcore PCI suspend/resume logic behave much better. In particular: - Even HCs without PCI PM support will normally be able to support global suspend, saving power ... and will need to resume later. Let them try to suspend; lots of not-that-old USB controllers don't have PM caps. - Saner order for the boilerplate PCI stuff. It also explicitly disables the IRQ and DMA, which aren't available in D1/D2/D3 states anyway. - Uses pci_enable_wake() when the root hub supports remote wakeup. Didn't fully work in one test setup; that controller's PME# was evidently ignored. (Not enabled unless CONFIG_USB_SUSPEND.) It worked for me with brief tests with the current 2.6.6-rc uhci-hcd with one old UHCI; more extensive ones with various OHCIs (using patches which I'll post soonish); and not at all with EHCI (where PM hasn't ever worked). Those of you who've been having PM problems might find this helpful as-is, though I think that unless you're using UHCI you'll also need an HCD patch. - Dave
-rw-r--r--drivers/usb/core/hcd-pci.c69
1 files changed, 47 insertions, 22 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 0aa8af55ef48..f9ae1e5851a4 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -279,15 +279,18 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
{
struct usb_hcd *hcd;
int retval = 0;
+ int has_pci_pm;
hcd = pci_get_drvdata(dev);
- dev_dbg (hcd->self.controller, "suspend D%d --> D%d\n",
- dev->current_state, state);
- if (pci_find_capability(dev, PCI_CAP_ID_PM)) {
- dev_dbg(hcd->self.controller, "No PM capability\n");
- return 0;
- }
+ /* even when the PCI layer rejects some of the PCI calls
+ * below, HCs can try global suspend and reduce DMA traffic.
+ * PM-sensitive HCDs may already have done this.
+ */
+ has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+ if (has_pci_pm)
+ dev_dbg(hcd->self.controller, "suspend D%d --> D%d\n",
+ dev->current_state, state);
switch (hcd->state) {
case USB_STATE_HALT:
@@ -297,23 +300,32 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
dev_dbg (hcd->self.controller, "hcd already suspended\n");
break;
default:
- /* remote wakeup needs hub->suspend() cooperation */
- // pci_enable_wake (dev, 3, 1);
-
- pci_save_state (dev, hcd->pci_state);
-
- /* driver may want to disable DMA etc */
- hcd->state = USB_STATE_QUIESCING;
retval = hcd->driver->suspend (hcd, state);
if (retval)
dev_dbg (hcd->self.controller,
"suspend fail, retval %d\n",
retval);
- else
+ else {
hcd->state = HCD_STATE_SUSPENDED;
+ pci_save_state (dev, hcd->pci_state);
+#ifdef CONFIG_USB_SUSPEND
+ pci_enable_wake (dev, state, hcd->remote_wakeup);
+ pci_enable_wake (dev, 4, hcd->remote_wakeup);
+#endif
+ /* no DMA or IRQs except in D0 */
+ pci_disable_device (dev);
+ free_irq (hcd->irq, hcd);
+
+ if (has_pci_pm)
+ retval = pci_set_power_state (dev, state);
+ if (retval < 0) {
+ dev_dbg (&dev->dev,
+ "PCI suspend fail, %d\n",
+ retval);
+ (void) usb_hcd_pci_resume (dev);
+ }
+ }
}
-
- pci_set_power_state (dev, state);
return retval;
}
EXPORT_SYMBOL (usb_hcd_pci_suspend);
@@ -328,10 +340,13 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;
+ int has_pci_pm;
hcd = pci_get_drvdata(dev);
- dev_dbg (hcd->self.controller, "resume from state D%d\n",
- dev->current_state);
+ has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+ if (has_pci_pm)
+ dev_dbg(hcd->self.controller, "resume from state D%d\n",
+ dev->current_state);
if (hcd->state != HCD_STATE_SUSPENDED) {
dev_dbg (hcd->self.controller,
@@ -340,11 +355,21 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
}
hcd->state = USB_STATE_RESUMING;
- pci_set_power_state (dev, 0);
+ if (has_pci_pm)
+ pci_set_power_state (dev, 0);
+ retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
+ hcd->description, hcd);
+ if (retval < 0) {
+ dev_err (hcd->self.controller,
+ "can't restore IRQ after resume!\n");
+ return retval;
+ }
+ pci_set_master (dev);
pci_restore_state (dev, hcd->pci_state);
-
- /* remote wakeup needs hub->suspend() cooperation */
- // pci_enable_wake (dev, 3, 0);
+#ifdef CONFIG_USB_SUSPEND
+ pci_enable_wake (dev, dev->current_state, 0);
+ pci_enable_wake (dev, 4, 0);
+#endif
retval = hcd->driver->resume (hcd);
if (!HCD_IS_RUNNING (hcd->state)) {