With this and some other patches (OHCI, EHCI, and hub for starters), I'm seeing swsusp partially behaving with usb loaded.
This one makes the usbcore pci suspend/resume logic treat "legacy" devices the same as "pci pm" devices ... and vice versa. It's that second part which will be interesting to some folk: legacy devices never fail a suspend request, so now ones with pci-pm won't either. As a side effect, that means it works around osdl bugid 2886 (where the pm core code is using PCI-incompatible power states). - Dave
Handle "legacy" (non-PM) devices better - basically just suspend hub and act as if the hardware were pm-aware - update dev->current_state, per API spec - accept all suspend states (no power reductions though) Implement the PCI API better - use D3hot suspend if D1/D2 was requested but the hardware can't do that, to be just as forgiving as "legacy" devices (also doubles as a USB-specific workaround for OSDL bugid 2886). - handle "suspend deeper" (D1->D2, D2->D3hot, etc) Minor fixes: - when debugging, dump the PCI PM states by name ("D3hot" etc) - there are no states beyond D3cold Signed-off-by: David Brownell <[EMAIL PROTECTED]> --- xu26/drivers/usb/core/hcd-pci.c 2004-09-09 18:46:18.000000000 -0700 +++ gadget-2.6/drivers/usb/core/hcd-pci.c 2004-09-10 18:02:18.000000000 -0700 @@ -266,6 +266,18 @@ #ifdef CONFIG_PM +static char *pci_state(u32 state) +{ + switch (state) { + case 0: return "D0"; + case 1: return "D1"; + case 2: return "D2"; + case 3: return "D3hot"; + case 4: return "D3cold"; + } + return NULL; +} + /** * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD * @dev: USB Host Controller being suspended @@ -286,16 +298,34 @@ * 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); + if (state > 4) + state = 4; switch (hcd->state) { case USB_STATE_HALT: dev_dbg (hcd->self.controller, "halted; hcd not suspended\n"); break; case HCD_STATE_SUSPENDED: - dev_dbg (hcd->self.controller, "hcd already suspended\n"); + dev_dbg (hcd->self.controller, "PCI %s --> %s\n", + pci_state(dev->current_state), + pci_state(state)); + if (state > 3) + state = 3; + + if (state == dev->current_state) + break; + else if (state < dev->current_state) + retval = -EIO; + else if (has_pci_pm) + retval = pci_set_power_state (dev, state); + else + dev->current_state = state; + + if (retval == 0) + dev->dev.power.power_state = state; + else + dev_dbg (hcd->self.controller, + "re-suspend fail, %d\n", retval); break; default: retval = hcd->driver->suspend (hcd, state); @@ -306,22 +336,48 @@ 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) + if (has_pci_pm) { retval = pci_set_power_state (dev, state); - dev->dev.power.power_state = state; + + /* POLICY: mask D1/D2/D3hot differences; + * we know D3hot will always work. + */ + if (retval < 0 && state < 3) { + retval = pci_set_power_state (dev, 3); + if (retval == 0) + state = 3; + } + if (retval == 0) { + dev->dev.power.power_state = state; +#ifdef CONFIG_USB_SUSPEND + pci_enable_wake (dev, state, + hcd->remote_wakeup); + pci_enable_wake (dev, 4, + hcd->remote_wakeup); +#endif + } + } else { + if (state > 3) + state = 3; + dev->current_state = state; + dev->dev.power.power_state = state; + } if (retval < 0) { dev_dbg (&dev->dev, - "PCI suspend fail, %d\n", + "PCI %s suspend fail, %d\n", + pci_state(state), retval); (void) usb_hcd_pci_resume (dev); + } else { + dev_dbg(hcd->self.controller, + "suspended to PCI %s%s\n", + pci_state(state), + has_pci_pm ? " (PM)" : ""); } } } @@ -343,9 +399,10 @@ hcd = pci_get_drvdata(dev); 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); + + /* D3cold resume isn't usually reported this way... */ + dev_dbg(hcd->self.controller, "resume from PCI %s\n", + pci_state(dev->current_state)); if (hcd->state != HCD_STATE_SUSPENDED) { dev_dbg (hcd->self.controller, @@ -356,6 +413,8 @@ if (has_pci_pm) pci_set_power_state (dev, 0); + else + dev->current_state = 0; dev->dev.power.power_state = 0; retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, hcd->description, hcd);