]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge back PCI power management material for v5.3.
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 24 Jun 2019 08:11:27 +0000 (10:11 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 24 Jun 2019 08:11:27 +0000 (10:11 +0200)
1  2 
drivers/pci/pci-driver.c

diff --combined drivers/pci/pci-driver.c
index 98af9ecd4a90c728ebebabd6bcd96a581e06b657,dd656cf19a12ffb6753d32199c0e54638651650a..bd097ea5925c4e62d503a10c9fee51b8a2b4953f
@@@ -524,6 -524,7 +524,6 @@@ static void pci_pm_default_resume_early
        pci_power_up(pci_dev);
        pci_restore_state(pci_dev);
        pci_pme_restore(pci_dev);
 -      pci_fixup_device(pci_fixup_resume_early, pci_dev);
  }
  
  /*
@@@ -678,6 -679,7 +678,7 @@@ static bool pci_has_legacy_pm_support(s
  static int pci_pm_prepare(struct device *dev)
  {
        struct device_driver *drv = dev->driver;
+       struct pci_dev *pci_dev = to_pci_dev(dev);
  
        if (drv && drv->pm && drv->pm->prepare) {
                int error = drv->pm->prepare(dev);
                if (!error && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE))
                        return 0;
        }
-       return pci_dev_keep_suspended(to_pci_dev(dev));
+       if (pci_dev_need_resume(pci_dev))
+               return 0;
+       /*
+        * The PME setting needs to be adjusted here in case the direct-complete
+        * optimization is used with respect to this device.
+        */
+       pci_dev_adjust_pme(pci_dev);
+       return 1;
  }
  
  static void pci_pm_complete(struct device *dev)
@@@ -757,9 -767,11 +766,11 @@@ static int pci_pm_suspend(struct devic
         * better to resume the device from runtime suspend here.
         */
        if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
-           !pci_dev_keep_suspended(pci_dev)) {
+           pci_dev_need_resume(pci_dev)) {
                pm_runtime_resume(dev);
                pci_dev->state_saved = false;
+       } else {
+               pci_dev_adjust_pme(pci_dev);
        }
  
        if (pm->suspend) {
@@@ -830,16 -842,18 +841,16 @@@ static int pci_pm_suspend_noirq(struct 
  
        if (pci_dev->skip_bus_pm) {
                /*
 -               * The function is running for the second time in a row without
 +               * Either the device is a bridge with a child in D0 below it, or
 +               * the function is running for the second time in a row without
                 * going through full resume, which is possible only during
 -               * suspend-to-idle in a spurious wakeup case.  Moreover, the
 -               * device was originally left in D0, so its power state should
 -               * not be changed here and the device register values saved
 -               * originally should be restored on resume again.
 +               * suspend-to-idle in a spurious wakeup case.  The device should
 +               * be in D0 at this point, but if it is a bridge, it may be
 +               * necessary to save its state.
                 */
 -              pci_dev->state_saved = true;
 -      } else if (pci_dev->state_saved) {
 -              if (pci_dev->current_state == PCI_D0)
 -                      pci_dev->skip_bus_pm = true;
 -      } else {
 +              if (!pci_dev->state_saved)
 +                      pci_save_state(pci_dev);
 +      } else if (!pci_dev->state_saved) {
                pci_save_state(pci_dev);
                if (pci_power_manageable(pci_dev))
                        pci_prepare_to_sleep(pci_dev);
        dev_dbg(dev, "PCI PM: Suspend power state: %s\n",
                pci_power_name(pci_dev->current_state));
  
 +      if (pci_dev->current_state == PCI_D0) {
 +              pci_dev->skip_bus_pm = true;
 +              /*
 +               * Per PCI PM r1.2, table 6-1, a bridge must be in D0 if any
 +               * downstream device is in D0, so avoid changing the power state
 +               * of the parent bridge by setting the skip_bus_pm flag for it.
 +               */
 +              if (pci_dev->bus->self)
 +                      pci_dev->bus->self->skip_bus_pm = true;
 +      }
 +
 +      if (pci_dev->skip_bus_pm && !pm_suspend_via_firmware()) {
 +              dev_dbg(dev, "PCI PM: Skipped\n");
 +              goto Fixup;
 +      }
 +
        pci_pm_set_unknown_state(pci_dev);
  
        /*
@@@ -911,16 -909,7 +922,16 @@@ static int pci_pm_resume_noirq(struct d
        if (dev_pm_smart_suspend_and_suspended(dev))
                pm_runtime_set_active(dev);
  
 -      pci_pm_default_resume_early(pci_dev);
 +      /*
 +       * In the suspend-to-idle case, devices left in D0 during suspend will
 +       * stay in D0, so it is not necessary to restore or update their
 +       * configuration here and attempting to put them into D0 again may
 +       * confuse some firmware, so avoid doing that.
 +       */
 +      if (!pci_dev->skip_bus_pm || pm_suspend_via_firmware())
 +              pci_pm_default_resume_early(pci_dev);
 +
 +      pci_fixup_device(pci_fixup_resume_early, pci_dev);
  
        if (pci_has_legacy_pm_support(pci_dev))
                return pci_legacy_resume_early(dev);
@@@ -1130,10 -1119,13 +1141,13 @@@ static int pci_pm_poweroff(struct devic
  
        /* The reason to do that is the same as in pci_pm_suspend(). */
        if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
-           !pci_dev_keep_suspended(pci_dev))
+           pci_dev_need_resume(pci_dev)) {
                pm_runtime_resume(dev);
+               pci_dev->state_saved = false;
+       } else {
+               pci_dev_adjust_pme(pci_dev);
+       }
  
-       pci_dev->state_saved = false;
        if (pm->poweroff) {
                int error;
  
@@@ -1216,7 -1208,6 +1230,7 @@@ static int pci_pm_restore_noirq(struct 
        }
  
        pci_pm_default_resume_early(pci_dev);
 +      pci_fixup_device(pci_fixup_resume_early, pci_dev);
  
        if (pci_has_legacy_pm_support(pci_dev))
                return pci_legacy_resume_early(dev);