|
@@ -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);
|
|
|
|