PCI PM: Introduce pci_prepare_to_sleep and pci_back_from_sleep
authorRafael J. Wysocki <rjw@sisk.pl>
Mon, 7 Jul 2008 01:35:26 +0000 (03:35 +0200)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Mon, 7 Jul 2008 23:26:33 +0000 (16:26 -0700)
Introduce functions pci_prepare_to_sleep() and pci_back_from_sleep(),
to be used by the PCI drivers that want to place their devices into
the lowest power state appropiate for them (PCI_D3hot, if the device
is not supposed to wake up the system, or the deepest state from
which the wake-up is possible, otherwise) while the system is being
prepared to go into a sleeping state and to put them back into D0
during the subsequent transition to the working state.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/pci.c
include/linux/pci.h

index a6b1b6f96abc6c7124b9b22b3ffec56a3513d462..1b0ec45e9934f2aa05c951d716ac1a4f2908c0ee 100644 (file)
@@ -1146,6 +1146,87 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
        return pme_done ? 0 : error;
 }
 
+/**
+ * pci_prepare_to_sleep - prepare PCI device for system-wide transition into
+ *                        a sleep state
+ * @dev: Device to handle.
+ *
+ * Choose the power state appropriate for the device depending on whether
+ * it can wake up the system and/or is power manageable by the platform
+ * (PCI_D3hot is the default) and put the device into that state.
+ */
+int pci_prepare_to_sleep(struct pci_dev *dev)
+{
+       pci_power_t target_state = PCI_D3hot;
+       int pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+       int error;
+
+       if (platform_pci_power_manageable(dev)) {
+               /*
+                * Call the platform to choose the target state of the device
+                * and enable wake-up from this state if supported.
+                */
+               pci_power_t state = platform_pci_choose_state(dev);
+
+               switch (state) {
+               case PCI_POWER_ERROR:
+               case PCI_UNKNOWN:
+                       break;
+               case PCI_D1:
+               case PCI_D2:
+                       if (pci_no_d1d2(dev))
+                               break;
+               default:
+                       target_state = state;
+                       pci_enable_wake(dev, target_state, true);
+               }
+       } else if (device_may_wakeup(&dev->dev)) {
+               /*
+                * Find the deepest state from which the device can generate
+                * wake-up events, make it the target state and enable device
+                * to generate PME#.
+                */
+               u16 pmc;
+
+               if (!pm)
+                       return -EIO;
+
+               pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
+               if (pmc & PCI_PM_CAP_PME_MASK) {
+                       if (!(pmc & PCI_PM_CAP_PME_D3)) {
+                               /* Device cannot generate PME# from D3_hot */
+                               if (pmc & PCI_PM_CAP_PME_D2)
+                                       target_state = PCI_D2;
+                               else if (pmc & PCI_PM_CAP_PME_D1)
+                                       target_state = PCI_D1;
+                               else
+                                       target_state = PCI_D0;
+                       }
+                       pci_pme_active(dev, pm, true);
+               }
+       }
+
+       error = pci_set_power_state(dev, target_state);
+
+       if (error)
+               pci_enable_wake(dev, target_state, false);
+
+       return error;
+}
+
+/**
+ * pci_back_from_sleep - turn PCI device on during system-wide transition into
+ *                       the working state a sleep state
+ * @dev: Device to handle.
+ *
+ * Disable device's sytem wake-up capability and put it into D0.
+ */
+int pci_back_from_sleep(struct pci_dev *dev)
+{
+       pci_enable_wake(dev, PCI_D0, false);
+       return pci_set_power_state(dev, PCI_D0);
+}
+
 /**
  * pci_pm_init - Initialize PM functions of given PCI device
  * @dev: PCI device to handle.
@@ -1853,5 +1934,7 @@ EXPORT_SYMBOL(pci_set_power_state);
 EXPORT_SYMBOL(pci_save_state);
 EXPORT_SYMBOL(pci_restore_state);
 EXPORT_SYMBOL(pci_enable_wake);
+EXPORT_SYMBOL(pci_prepare_to_sleep);
+EXPORT_SYMBOL(pci_back_from_sleep);
 EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
 
index 4c80dc3f2990910622399a10127753dc4f6826e1..52ac06dcce98f71171bd862cde7faf9a54c941c1 100644 (file)
@@ -632,6 +632,8 @@ int pci_restore_state(struct pci_dev *dev);
 int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
 pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
 int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable);
+int pci_prepare_to_sleep(struct pci_dev *dev);
+int pci_back_from_sleep(struct pci_dev *dev);
 
 /* Functions for PCI Hotplug drivers to use */
 int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);