/** * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD * @dev: USB Host Controller being suspended * @state: state that the controller is going into * * Store this function in the HCD's struct pci_driver as suspend(). */ 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); /* 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: dev_dbg (hcd->self.controller, "halted; hcd not suspended\n"); break; case HCD_STATE_SUSPENDED: dev_dbg (hcd->self.controller, "hcd already suspended\n"); break; default: retval = hcd->driver->suspend (hcd, state); if (retval) dev_dbg (hcd->self.controller, "suspend fail, retval %d\n", retval); 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); dev->dev.power.power_state = state; if (retval < 0) { dev_dbg (&dev->dev, "PCI suspend fail, %d\n", retval); (void) usb_hcd_pci_resume (dev); } } } return retval; }
/* * test_hcd_resume * make call to resume device for power management * so that device will be active and able to use * again */ static int test_hcd_resume() { int rc; struct pci_dev *pdev = NULL; /* check that pdev is set */ if (!(pdev = ltp_usb.pdev)) { printk("tusb: Cant find host controller pci_dev pointer\n"); return 1; } /* make call and check return value */ rc = usb_hcd_pci_resume(pdev); if (rc) printk("tusb: Resume got retval, failure\n"); else printk("tusb: Resume success\n"); return rc; }
/** * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD * @dev: USB Host Controller being suspended * @state: state that the controller is going into * * Store this function in the HCD's struct pci_driver as suspend(). */ 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); /* 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 (state > 4) state = 4; switch (hcd->state) { /* entry if root hub wasn't yet suspended ... from sysfs, * without autosuspend, or if USB_SUSPEND isn't configured. */ case USB_STATE_RUNNING: hcd->state = USB_STATE_QUIESCING; retval = hcd->driver->suspend (hcd, state); if (retval) { dev_dbg (hcd->self.controller, "suspend fail, retval %d\n", retval); break; } hcd->state = HCD_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 HCD_STATE_SUSPENDED: if (state <= dev->current_state) break; /* no DMA or IRQs except in D0 */ if (!dev->current_state) { pci_save_state (dev); pci_disable_device (dev); free_irq (hcd->irq, hcd); } if (!has_pci_pm) { dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); break; } /* POLICY: ignore D1/D2/D3hot differences; * we know D3hot will always work. */ retval = pci_set_power_state (dev, state); if (retval < 0 && state < 3) { retval = pci_set_power_state (dev, 3); if (retval == 0) state = 3; } if (retval == 0) { dev_dbg (hcd->self.controller, "--> PCI %s\n", pci_state(dev->current_state)); #ifdef CONFIG_USB_SUSPEND pci_enable_wake (dev, state, hcd->remote_wakeup); pci_enable_wake (dev, 4, hcd->remote_wakeup); #endif } else if (retval < 0) { dev_dbg (&dev->dev, "PCI %s suspend fail, %d\n", pci_state(state), retval); (void) usb_hcd_pci_resume (dev); break; } break; default: dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", hcd->state); retval = -EINVAL; break; } /* update power_state **ONLY** to make sysfs happier */ if (retval == 0) dev->dev.power.power_state = state; return retval; }
/** * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD * @dev: USB Host Controller being suspended * @message: semantics in flux * * Store this function in the HCD's struct pci_driver as suspend(). */ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) { struct usb_hcd *hcd; int retval = 0; int has_pci_pm; hcd = pci_get_drvdata(dev); /* 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 * will need to provide a target system state again. * * It'll be important to learn characteristics of the target state, * especially on embedded hardware where the HCD will often be in * charge of an external VBUS power supply and one or more clocks. * Some target system states will leave them active; others won't. * (With PCI, that's often handled by platform BIOS code.) */ /* 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); switch (hcd->state) { /* entry if root hub wasn't yet suspended ... from sysfs, * without autosuspend, or if USB_SUSPEND isn't configured. */ 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 */ /* 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) { free_irq (hcd->irq, hcd); pci_save_state (dev); pci_disable_device (dev); } if (!has_pci_pm) { dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); break; } /* 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); if (retval == 0) { dev_dbg (hcd->self.controller, "--> PCI D3\n"); pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup); pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup); } else if (retval < 0) { dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n", retval); (void) usb_hcd_pci_resume (dev); break; } break; default: 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 */ if (retval == 0) dev->dev.power.power_state = message; return retval; }
/** * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD * @dev: USB Host Controller being suspended * @message: semantics in flux * * Store this function in the HCD's struct pci_driver as suspend(). */ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) { struct usb_hcd *hcd; int retval = 0; int has_pci_pm; 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->state == HC_STATE_SUSPENDED || hcd->state == HC_STATE_HALT)) return -EBUSY; if (hcd->driver->suspend) { retval = hcd->driver->suspend(hcd, message); suspend_report_result(hcd->driver->suspend, retval); if (retval) goto done; } synchronize_irq(dev->irq); /* 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 * will need to provide a target system state again. * * It'll be important to learn characteristics of the target state, * especially on embedded hardware where the HCD will often be in * charge of an external VBUS power supply and one or more clocks. * Some target system states will leave them active; others won't. * (With PCI, that's often handled by platform BIOS code.) */ /* 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); /* 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. */ if (hcd->state == HC_STATE_SUSPENDED) { /* no DMA or IRQs except when HC is active */ if (dev->current_state == PCI_D0) { pci_save_state (dev); pci_disable_device (dev); } if (!has_pci_pm) { dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); goto done; } /* 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) { int wake = device_can_wakeup(&hcd->self.root_hub->dev); wake = wake && device_may_wakeup(hcd->self.controller); dev_dbg (hcd->self.controller, "--> PCI D3%s\n", wake ? "/wakeup" : ""); /* 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, wake); (void) pci_enable_wake (dev, PCI_D3cold, wake); } else { dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n", retval); (void) usb_hcd_pci_resume (dev); } } else if (hcd->state != HC_STATE_HALT) { dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", hcd->state); WARN_ON(1); retval = -EINVAL; } done: if (retval == 0) { dev->dev.power.power_state = PMSG_SUSPEND; #ifdef CONFIG_PPC_PMAC /* 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); } #endif } return retval; }