vgaarb: use MIT license
[safe/jmp/linux-2.6] / drivers / pci / pci-driver.c
index e5d47be..f9a0aec 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/cpu.h>
+#include <linux/pm_runtime.h>
 #include "pci.h"
 
 struct pci_dynid {
@@ -404,6 +405,35 @@ static void pci_device_shutdown(struct device *dev)
        pci_msix_shutdown(pci_dev);
 }
 
+#ifdef CONFIG_PM_OPS
+
+/* Auxiliary functions used for system resume and run-time resume. */
+
+/**
+ * pci_restore_standard_config - restore standard config registers of PCI device
+ * @pci_dev: PCI device to handle
+ */
+static int pci_restore_standard_config(struct pci_dev *pci_dev)
+{
+       pci_update_current_state(pci_dev, PCI_UNKNOWN);
+
+       if (pci_dev->current_state != PCI_D0) {
+               int error = pci_set_power_state(pci_dev, PCI_D0);
+               if (error)
+                       return error;
+       }
+
+       return pci_restore_state(pci_dev);
+}
+
+static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
+{
+       pci_restore_standard_config(pci_dev);
+       pci_fixup_device(pci_fixup_resume_early, pci_dev);
+}
+
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 
 /*
@@ -520,29 +550,6 @@ static int pci_legacy_resume(struct device *dev)
 
 /* Auxiliary functions used by the new power management framework */
 
-/**
- * pci_restore_standard_config - restore standard config registers of PCI device
- * @pci_dev: PCI device to handle
- */
-static int pci_restore_standard_config(struct pci_dev *pci_dev)
-{
-       pci_update_current_state(pci_dev, PCI_UNKNOWN);
-
-       if (pci_dev->current_state != PCI_D0) {
-               int error = pci_set_power_state(pci_dev, PCI_D0);
-               if (error)
-                       return error;
-       }
-
-       return pci_restore_state(pci_dev);
-}
-
-static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
-{
-       pci_restore_standard_config(pci_dev);
-       pci_fixup_device(pci_fixup_resume_early, pci_dev);
-}
-
 static void pci_pm_default_resume(struct pci_dev *pci_dev)
 {
        pci_fixup_device(pci_fixup_resume, pci_dev);
@@ -581,6 +588,17 @@ static int pci_pm_prepare(struct device *dev)
        struct device_driver *drv = dev->driver;
        int error = 0;
 
+       /*
+        * PCI devices suspended at run time need to be resumed at this
+        * point, because in general it is necessary to reconfigure them for
+        * system suspend.  Namely, if the device is supposed to wake up the
+        * system from the sleep state, we may need to reconfigure it for this
+        * purpose.  In turn, if the device is not supposed to wake up the
+        * system from the sleep state, we'll have to prevent it from signaling
+        * wake-up.
+        */
+       pm_runtime_resume(dev);
+
        if (drv && drv->pm && drv->pm->prepare)
                error = drv->pm->prepare(dev);
 
@@ -595,6 +613,13 @@ static void pci_pm_complete(struct device *dev)
                drv->pm->complete(dev);
 }
 
+#else /* !CONFIG_PM_SLEEP */
+
+#define pci_pm_prepare NULL
+#define pci_pm_complete        NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
 #ifdef CONFIG_SUSPEND
 
 static int pci_pm_suspend(struct device *dev)
@@ -681,7 +706,7 @@ static int pci_pm_resume_noirq(struct device *dev)
        struct device_driver *drv = dev->driver;
        int error = 0;
 
-       pci_pm_default_resume_noirq(pci_dev);
+       pci_pm_default_resume_early(pci_dev);
 
        if (pci_has_legacy_pm_support(pci_dev))
                return pci_legacy_resume_early(dev);
@@ -879,7 +904,7 @@ static int pci_pm_restore_noirq(struct device *dev)
        struct device_driver *drv = dev->driver;
        int error = 0;
 
-       pci_pm_default_resume_noirq(pci_dev);
+       pci_pm_default_resume_early(pci_dev);
 
        if (pci_has_legacy_pm_support(pci_dev))
                return pci_legacy_resume_early(dev);
@@ -931,6 +956,84 @@ static int pci_pm_restore(struct device *dev)
 
 #endif /* !CONFIG_HIBERNATION */
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int pci_pm_runtime_suspend(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       pci_power_t prev = pci_dev->current_state;
+       int error;
+
+       if (!pm || !pm->runtime_suspend)
+               return -ENOSYS;
+
+       error = pm->runtime_suspend(dev);
+       suspend_report_result(pm->runtime_suspend, error);
+       if (error)
+               return error;
+
+       pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+       if (!pci_dev->state_saved && 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->runtime_suspend);
+               return 0;
+       }
+
+       if (!pci_dev->state_saved)
+               pci_save_state(pci_dev);
+
+       pci_finish_runtime_suspend(pci_dev);
+
+       return 0;
+}
+
+static int pci_pm_runtime_resume(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+       if (!pm || !pm->runtime_resume)
+               return -ENOSYS;
+
+       pci_pm_default_resume_early(pci_dev);
+       __pci_enable_wake(pci_dev, PCI_D0, true, false);
+       pci_fixup_device(pci_fixup_resume, pci_dev);
+
+       return pm->runtime_resume(dev);
+}
+
+static int pci_pm_runtime_idle(struct device *dev)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+       if (!pm)
+               return -ENOSYS;
+
+       if (pm->runtime_idle) {
+               int ret = pm->runtime_idle(dev);
+               if (ret)
+                       return ret;
+       }
+
+       pm_runtime_suspend(dev);
+
+       return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+#define pci_pm_runtime_suspend NULL
+#define pci_pm_runtime_resume  NULL
+#define pci_pm_runtime_idle    NULL
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_OPS
+
 const struct dev_pm_ops pci_dev_pm_ops = {
        .prepare = pci_pm_prepare,
        .complete = pci_pm_complete,
@@ -946,15 +1049,18 @@ const struct dev_pm_ops pci_dev_pm_ops = {
        .thaw_noirq = pci_pm_thaw_noirq,
        .poweroff_noirq = pci_pm_poweroff_noirq,
        .restore_noirq = pci_pm_restore_noirq,
+       .runtime_suspend = pci_pm_runtime_suspend,
+       .runtime_resume = pci_pm_runtime_resume,
+       .runtime_idle = pci_pm_runtime_idle,
 };
 
 #define PCI_PM_OPS_PTR (&pci_dev_pm_ops)
 
-#else /* !CONFIG_PM_SLEEP */
+#else /* !COMFIG_PM_OPS */
 
 #define PCI_PM_OPS_PTR NULL
 
-#endif /* !CONFIG_PM_SLEEP */
+#endif /* !COMFIG_PM_OPS */
 
 /**
  * __pci_register_driver - register a new pci driver