From: Rafael J. Wysocki <rafael.j.wyso...@intel.com>

Make the PCI bus type take DPM_FLAG_SMART_SUSPEND into account in its
system suspend callbacks and make sure that all code that should not
run in parallel with pci_pm_runtime_resume() is executed in the "late"
phases of system suspend, freeze and poweroff transitions.

[Note that the pm_runtime_suspended() check in pci_dev_keep_suspended()
is an optimization, because if is not passed, all of the subsequent
checks may be skipped and some of them are much more overhead in
general.]

Signed-off-by: Rafael J. Wysocki <rafael.j.wyso...@intel.com>
---
---
 Documentation/power/pci.txt |    6 ++++
 drivers/pci/pci-driver.c    |   56 ++++++++++++++++++++++++++++++--------------
 2 files changed, 45 insertions(+), 17 deletions(-)

Index: linux-pm/drivers/pci/pci-driver.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-driver.c
+++ linux-pm/drivers/pci/pci-driver.c
@@ -727,18 +727,25 @@ static int pci_pm_suspend(struct device
 
        if (!pm) {
                pci_pm_default_suspend(pci_dev);
-               goto Fixup;
+               return 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.
+        * PCI devices suspended at run time may need to be resumed at this
+        * point, because in general it may be necessary to reconfigure them for
+        * system suspend.  Namely, if the device is expected to wake up the
+        * system from the sleep state, it may have to be reconfigured for this
+        * purpose, or if the device is not expected to wake up the system from
+        * the sleep state, it should be prevented from signaling wakeup events
+        * going forward.
+        *
+        * Also if the driver of the device does not indicate that its system
+        * suspend callbacks can cope with runtime-suspended devices, it is
+        * better to resume the device from runtime suspend here.
         */
-       pm_runtime_resume(dev);
+       if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+           !pci_dev_keep_suspended(pci_dev))
+               pm_runtime_resume(dev);
 
        pci_dev->state_saved = false;
        if (pm->suspend) {
@@ -758,12 +765,16 @@ static int pci_pm_suspend(struct device
                }
        }
 
- Fixup:
-       pci_fixup_device(pci_fixup_suspend, pci_dev);
-
        return 0;
 }
 
+static int pci_pm_suspend_late(struct device *dev)
+{
+       pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
+
+       return pm_generic_suspend_late(dev);;
+}
+
 static int pci_pm_suspend_noirq(struct device *dev)
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -872,6 +883,7 @@ static int pci_pm_resume(struct device *
 #else /* !CONFIG_SUSPEND */
 
 #define pci_pm_suspend         NULL
+#define pci_pm_suspend_late    NULL
 #define pci_pm_suspend_noirq   NULL
 #define pci_pm_resume          NULL
 #define pci_pm_resume_noirq    NULL
@@ -906,7 +918,8 @@ static int pci_pm_freeze(struct device *
         * devices should not be touched during freeze/thaw transitions,
         * however.
         */
-       pm_runtime_resume(dev);
+       if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
+               pm_runtime_resume(dev);
 
        pci_dev->state_saved = false;
        if (pm->freeze) {
@@ -1004,11 +1017,13 @@ static int pci_pm_poweroff(struct device
 
        if (!pm) {
                pci_pm_default_suspend(pci_dev);
-               goto Fixup;
+               return 0;
        }
 
        /* The reason to do that is the same as in pci_pm_suspend(). */
-       pm_runtime_resume(dev);
+       if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
+           !pci_dev_keep_suspended(pci_dev))
+               pm_runtime_resume(dev);
 
        pci_dev->state_saved = false;
        if (pm->poweroff) {
@@ -1020,12 +1035,16 @@ static int pci_pm_poweroff(struct device
                        return error;
        }
 
- Fixup:
-       pci_fixup_device(pci_fixup_suspend, pci_dev);
-
        return 0;
 }
 
+static int pci_pm_poweroff_late(struct device *dev)
+{
+       pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
+
+       return pm_generic_poweroff_late(dev);
+}
+
 static int pci_pm_poweroff_noirq(struct device *dev)
 {
        struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -1124,6 +1143,7 @@ static int pci_pm_restore(struct device
 #define pci_pm_thaw            NULL
 #define pci_pm_thaw_noirq      NULL
 #define pci_pm_poweroff                NULL
+#define pci_pm_poweroff_late   NULL
 #define pci_pm_poweroff_noirq  NULL
 #define pci_pm_restore         NULL
 #define pci_pm_restore_noirq   NULL
@@ -1239,10 +1259,12 @@ static const struct dev_pm_ops pci_dev_p
        .prepare = pci_pm_prepare,
        .complete = pci_pm_complete,
        .suspend = pci_pm_suspend,
+       .suspend_late = pci_pm_suspend_late,
        .resume = pci_pm_resume,
        .freeze = pci_pm_freeze,
        .thaw = pci_pm_thaw,
        .poweroff = pci_pm_poweroff,
+       .poweroff_late = pci_pm_poweroff_late,
        .restore = pci_pm_restore,
        .suspend_noirq = pci_pm_suspend_noirq,
        .resume_noirq = pci_pm_resume_noirq,
Index: linux-pm/Documentation/power/pci.txt
===================================================================
--- linux-pm.orig/Documentation/power/pci.txt
+++ linux-pm/Documentation/power/pci.txt
@@ -980,6 +980,12 @@ positive value from pci_pm_prepare() if
 driver of the device returns a positive value.  That allows the driver to opt
 out from using the direct-complete mechanism dynamically.
 
+The DPM_FLAG_SMART_SUSPEND flag tells the PCI bus type that from the driver's
+perspective the device can be safely left in runtime suspend during system
+suspend.  That causes pci_pm_suspend(), pci_pm_freeze() and pci_pm_poweroff()
+to skip resuming the device from runtime suspend unless there are PCI-specific
+reasons for doing that.
+
 3.2. Device Runtime Power Management
 ------------------------------------
 In addition to providing device power management callbacks PCI device drivers


Reply via email to