>From 59d37d8db8630a0ad6ee5eaf322abb08700117f5 Mon Sep 17 00:00:00 2001 From: Andy Luo <[email protected]> Date: Wed, 24 Nov 2010 16:43:37 +0800 Subject: [PATCH] usb: core: add runtime pm support
This patch enables runtime pm support for usb host driver Signed-off-by: Andy Luo <[email protected]> --- drivers/usb/core/hcd-pci.c | 197 +++++++++++++++++++++++++++++++------------- 1 files changed, 140 insertions(+), 57 deletions(-) diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 1cf2d1e..ed1515f 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -250,6 +250,10 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) if (retval != 0) goto err4; set_hs_companion(dev, hcd); + + pm_runtime_set_active(&dev->dev); + pm_runtime_enable(&dev->dev); + pm_runtime_allow(&dev->dev); return retval; err4: @@ -292,6 +296,18 @@ void usb_hcd_pci_remove(struct pci_dev *dev) if (!hcd) return; + pm_runtime_forbid(&dev->dev); + pm_runtime_disable(&dev->dev); + pm_runtime_set_suspended(&dev->dev); + + /* Fake an interrupt request in order to give the driver a chance + * to test whether the controller hardware has been removed (e.g., + * cardbus physical eject). + */ + local_irq_disable(); + usb_hcd_irq(0, hcd); + local_irq_enable(); + usb_remove_hcd(hcd); if (hcd->driver->flags & HCD_MEMORY) { iounmap(hcd->regs); @@ -317,12 +333,48 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) if (!hcd) return; - if (hcd->driver->shutdown) + if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && + hcd->driver->shutdown) hcd->driver->shutdown(hcd); } EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM_OPS + +#ifdef CONFIG_PPC_PMAC +static void pci_power_config(struct pci_dev *pci_dev, int enable) +{ + /* Enanble or 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, enable); + } +} + +#elif defined(CONFIG_X86_MRST) + +static inline void pci_power_config(struct pci_dev *pci_dev, int enable) +{ + /* MRST/MFLD */ + if (enable) { + pci_restore_state(pci_dev); + pci_set_power_state(pci_dev, PCI_D0); + } else { + pci_save_state(pci_dev); + pci_set_power_state(pci_dev, PCI_D3hot); + } +} + +#else + +static inline void pci_power_config(struct pci_dev *pci_dev, int enable) +{} + +#endif /* CONFIG_PPC_PMAC */ static int check_root_hub_suspended(struct device *dev) { @@ -337,7 +389,7 @@ static int check_root_hub_suspended(struct device *dev) return 0; } -static int hcd_pci_suspend(struct device *dev) +static int suspend_common(struct device *dev, bool do_wakeup) { struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); @@ -374,6 +426,48 @@ static int hcd_pci_suspend(struct device *dev) return retval; } +static int resume_common(struct device *dev, int event) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + int retval; + + if (hcd->state != HC_STATE_SUSPENDED) { + dev_dbg(dev, "can't resume, not suspended!\n"); + return 0; + } + + retval = pci_enable_device(pci_dev); + if (retval < 0) { + dev_err(dev, "can't re-enable after resume, %d!\n", retval); + return retval; + } + + pci_set_master(pci_dev); + + clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + + if (hcd->driver->pci_resume) { + if (event != PM_EVENT_AUTO_RESUME) + wait_for_companions(pci_dev, hcd); + + retval = hcd->driver->pci_resume(hcd, + event == PM_EVENT_RESTORE); + if (retval) { + dev_err(dev, "PCI post-resume error %d!\n", retval); + usb_hc_died(hcd); + } + } + return retval; +} + +#ifdef CONFIG_PM_SLEEP + +static int hcd_pci_suspend(struct device *dev) +{ + return suspend_common(dev, device_may_wakeup(dev)); +} + static int hcd_pci_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -408,16 +502,7 @@ static int hcd_pci_suspend_noirq(struct device *dev) return retval; } -#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(pci_dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); - } -#endif + pci_power_config(pci_dev, 0); return retval; } @@ -425,69 +510,65 @@ static int hcd_pci_resume_noirq(struct device *dev) { 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(pci_dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, - of_node, 0, 1); - } -#endif + pci_power_config(pci_dev, 1); /* 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) +static int hcd_pci_resume(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - struct usb_hcd *hcd = pci_get_drvdata(pci_dev); - int retval; + return resume_common(dev, PM_EVENT_RESUME); +} - if (hcd->state != HC_STATE_SUSPENDED) { - dev_dbg(dev, "can't resume, not suspended!\n"); - return 0; - } +static int hcd_pci_restore(struct device *dev) +{ + return resume_common(dev, PM_EVENT_RESTORE); +} - retval = pci_enable_device(pci_dev); - if (retval < 0) { - dev_err(dev, "can't re-enable after resume, %d!\n", retval); - return retval; - } +#else - pci_set_master(pci_dev); +#define hcd_pci_suspend NULL +#define hcd_pci_suspend_noirq NULL +#define hcd_pci_resume_noirq NULL +#define hcd_pci_resume NULL +#define hcd_pci_restore NULL - clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); +#endif /* CONFIG_PM_SLEEP */ - if (hcd->driver->pci_resume) { - /* This call should be made only during system resume, - * not during runtime resume. - */ - wait_for_companions(pci_dev, hcd); +#ifdef CONFIG_PM_RUNTIME - retval = hcd->driver->pci_resume(hcd, hibernated); - if (retval) { - dev_err(dev, "PCI post-resume error %d!\n", retval); - usb_hc_died(hcd); - } - } +static int hcd_pci_runtime_suspend(struct device *dev) +{ + int retval; + + device_set_wakeup_enable(dev, 1); + retval = suspend_common(dev, true); + if (retval == 0) + pci_power_config(to_pci_dev(dev), 0); + dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval); return retval; } -static int hcd_pci_resume(struct device *dev) +static int hcd_pci_runtime_resume(struct device *dev) { - return resume_common(dev, false); -} + int retval; -static int hcd_pci_restore(struct device *dev) -{ - return resume_common(dev, true); + device_set_wakeup_enable(dev, 0); + pci_power_config(to_pci_dev(dev), 1); + retval = resume_common(dev, PM_EVENT_RESTORE); + dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); + return retval; } +#else + +#define hcd_pci_runtime_suspend NULL +#define hcd_pci_runtime_resume NULL + +#endif /* CONFIG_PM_RUNTIME */ + const struct dev_pm_ops usb_hcd_pci_pm_ops = { .suspend = hcd_pci_suspend, .suspend_noirq = hcd_pci_suspend_noirq, @@ -501,7 +582,9 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = { .poweroff_noirq = hcd_pci_suspend_noirq, .restore_noirq = hcd_pci_resume_noirq, .restore = hcd_pci_restore, + .runtime_suspend = hcd_pci_runtime_suspend, + .runtime_resume = hcd_pci_runtime_resume, }; EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); -#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM_OPS */ -- 1.7.2.1
0002-usb-core-add-runtime-pm-support.patch
Description: 0002-usb-core-add-runtime-pm-support.patch
_______________________________________________ MeeGo-kernel mailing list [email protected] http://lists.meego.com/listinfo/meego-kernel
