diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-02-07 10:46:30 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-02-07 10:46:30 -0800 |
commit | e83102cab0fd95d4508361b061146c978b3abd60 (patch) | |
tree | d1452292bf5d55da6a072d36d507d2e918ee2180 /drivers/pci/pci-driver.c | |
parent | 7f9a50a5b89b87f8e754f59ae9968da28be618a5 (diff) | |
parent | 5294e256717923f4a3297bb8b802f5e0625763f3 (diff) | |
download | linux-e83102cab0fd95d4508361b061146c978b3abd60.tar.gz linux-e83102cab0fd95d4508361b061146c978b3abd60.tar.bz2 linux-e83102cab0fd95d4508361b061146c978b3abd60.zip |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6:
PCI PM: make the PM core more careful with drivers using the new PM framework
PCI PM: Read power state from device after trying to change it on resume
PCI PM: Do not disable and enable bridges during suspend-resume
PCI: PCIe portdrv: Simplify suspend and resume
PCI PM: Fix saving of device state in pci_legacy_suspend
PCI PM: Check if the state has been saved before trying to restore it
PCI PM: Fix handling of devices without drivers
PCI: return error on failure to read PCI ROMs
PCI: properly clean up ASPM link state on device remove
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r-- | drivers/pci/pci-driver.c | 164 |
1 files changed, 105 insertions, 59 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index ab1d615425a8..93eac1423585 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -355,6 +355,8 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) int i = 0; if (drv && drv->suspend) { + pci_power_t prev = pci_dev->current_state; + pci_dev->state_saved = false; i = drv->suspend(pci_dev, state); @@ -365,12 +367,16 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) if (pci_dev->state_saved) goto Fixup; - if (WARN_ON_ONCE(pci_dev->current_state != PCI_D0)) + if (pci_dev->current_state != PCI_D0 + && pci_dev->current_state != PCI_UNKNOWN) { + WARN_ONCE(pci_dev->current_state != prev, + "PCI PM: Device state not saved by %pF\n", + drv->suspend); goto Fixup; + } } pci_save_state(pci_dev); - pci_dev->state_saved = true; /* * This is for compatibility with existing code with legacy PM support. */ @@ -424,35 +430,20 @@ static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) pci_fixup_device(pci_fixup_resume_early, pci_dev); } -static int pci_pm_default_resume(struct pci_dev *pci_dev) +static void pci_pm_default_resume(struct pci_dev *pci_dev) { pci_fixup_device(pci_fixup_resume, pci_dev); if (!pci_is_bridge(pci_dev)) pci_enable_wake(pci_dev, PCI_D0, false); - - return pci_pm_reenable_device(pci_dev); -} - -static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) -{ - /* If device is enabled at this point, disable it */ - pci_disable_enabled_device(pci_dev); - /* - * Save state with interrupts enabled, because in principle the bus the - * device is on may be put into a low power state after this code runs. - */ - pci_save_state(pci_dev); } static void pci_pm_default_suspend(struct pci_dev *pci_dev) { - pci_pm_default_suspend_generic(pci_dev); - + /* Disable non-bridge devices without PM support */ if (!pci_is_bridge(pci_dev)) - pci_prepare_to_sleep(pci_dev); - - pci_fixup_device(pci_fixup_suspend, pci_dev); + pci_disable_enabled_device(pci_dev); + pci_save_state(pci_dev); } static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) @@ -497,21 +488,49 @@ static void pci_pm_complete(struct device *dev) static int pci_pm_suspend(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; - int error = 0; + struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_SUSPEND); - if (drv && drv->pm && drv->pm->suspend) { - error = drv->pm->suspend(dev); - suspend_report_result(drv->pm->suspend, error); + if (!pm) { + pci_pm_default_suspend(pci_dev); + goto Fixup; } - if (!error) - pci_pm_default_suspend(pci_dev); + pci_dev->state_saved = false; - return error; + if (pm->suspend) { + pci_power_t prev = pci_dev->current_state; + int error; + + error = pm->suspend(dev); + suspend_report_result(pm->suspend, error); + if (error) + return error; + + if (pci_dev->state_saved) + goto Fixup; + + if (pci_dev->current_state != PCI_D0 + && pci_dev->current_state != PCI_UNKNOWN) { + WARN_ONCE(pci_dev->current_state != prev, + "PCI PM: State of device not saved by %pF\n", + pm->suspend); + goto Fixup; + } + } + + if (!pci_dev->state_saved) { + pci_save_state(pci_dev); + if (!pci_is_bridge(pci_dev)) + pci_prepare_to_sleep(pci_dev); + } + + Fixup: + pci_fixup_device(pci_fixup_suspend, pci_dev); + + return 0; } static int pci_pm_suspend_noirq(struct device *dev) @@ -554,7 +573,7 @@ static int pci_pm_resume_noirq(struct device *dev) static int pci_pm_resume(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; + struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; /* @@ -567,12 +586,16 @@ static int pci_pm_resume(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - error = pci_pm_default_resume(pci_dev); + pci_pm_default_resume(pci_dev); - if (!error && drv && drv->pm && drv->pm->resume) - error = drv->pm->resume(dev); + if (pm) { + if (pm->resume) + error = pm->resume(dev); + } else { + pci_pm_reenable_device(pci_dev); + } - return error; + return 0; } #else /* !CONFIG_SUSPEND */ @@ -589,21 +612,31 @@ static int pci_pm_resume(struct device *dev) static int pci_pm_freeze(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; - int error = 0; + struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_FREEZE); - if (drv && drv->pm && drv->pm->freeze) { - error = drv->pm->freeze(dev); - suspend_report_result(drv->pm->freeze, error); + if (!pm) { + pci_pm_default_suspend(pci_dev); + return 0; } - if (!error) - pci_pm_default_suspend_generic(pci_dev); + pci_dev->state_saved = false; - return error; + if (pm->freeze) { + int error; + + error = pm->freeze(dev); + suspend_report_result(pm->freeze, error); + if (error) + return error; + } + + if (!pci_dev->state_saved) + pci_save_state(pci_dev); + + return 0; } static int pci_pm_freeze_noirq(struct device *dev) @@ -646,16 +679,18 @@ static int pci_pm_thaw_noirq(struct device *dev) static int pci_pm_thaw(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; + struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - pci_pm_reenable_device(pci_dev); - - if (drv && drv->pm && drv->pm->thaw) - error = drv->pm->thaw(dev); + if (pm) { + if (pm->thaw) + error = pm->thaw(dev); + } else { + pci_pm_reenable_device(pci_dev); + } return error; } @@ -663,22 +698,29 @@ static int pci_pm_thaw(struct device *dev) static int pci_pm_poweroff(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; + struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_HIBERNATE); - if (!drv || !drv->pm) - return 0; + if (!pm) { + pci_pm_default_suspend(pci_dev); + goto Fixup; + } + + pci_dev->state_saved = false; - if (drv->pm->poweroff) { - error = drv->pm->poweroff(dev); - suspend_report_result(drv->pm->poweroff, error); + if (pm->poweroff) { + error = pm->poweroff(dev); + suspend_report_result(pm->poweroff, error); } - if (!error) - pci_pm_default_suspend(pci_dev); + if (!pci_dev->state_saved && !pci_is_bridge(pci_dev)) + pci_prepare_to_sleep(pci_dev); + + Fixup: + pci_fixup_device(pci_fixup_suspend, pci_dev); return error; } @@ -719,7 +761,7 @@ static int pci_pm_restore_noirq(struct device *dev) static int pci_pm_restore(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; + struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; /* @@ -732,10 +774,14 @@ static int pci_pm_restore(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - error = pci_pm_default_resume(pci_dev); + pci_pm_default_resume(pci_dev); - if (!error && drv && drv->pm && drv->pm->restore) - error = drv->pm->restore(dev); + if (pm) { + if (pm->restore) + error = pm->restore(dev); + } else { + pci_pm_reenable_device(pci_dev); + } return error; } |