|
@@ -539,6 +539,53 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * pci_platform_power_transition - Use platform to change device power state
|
|
|
+ * @dev: PCI device to handle.
|
|
|
+ * @state: State to put the device into.
|
|
|
+ */
|
|
|
+static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
|
+{
|
|
|
+ int error;
|
|
|
+
|
|
|
+ if (platform_pci_power_manageable(dev)) {
|
|
|
+ error = platform_pci_set_power_state(dev, state);
|
|
|
+ if (!error)
|
|
|
+ pci_update_current_state(dev, state);
|
|
|
+ } else {
|
|
|
+ error = -ENODEV;
|
|
|
+ /* Fall back to PCI_D0 if native PM is not supported */
|
|
|
+ pci_update_current_state(dev, PCI_D0);
|
|
|
+ }
|
|
|
+
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * __pci_start_power_transition - Start power transition of a PCI device
|
|
|
+ * @dev: PCI device to handle.
|
|
|
+ * @state: State to put the device into.
|
|
|
+ */
|
|
|
+static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
|
+{
|
|
|
+ if (state == PCI_D0)
|
|
|
+ pci_platform_power_transition(dev, PCI_D0);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * __pci_complete_power_transition - Complete power transition of a PCI device
|
|
|
+ * @dev: PCI device to handle.
|
|
|
+ * @state: State to put the device into.
|
|
|
+ *
|
|
|
+ * This function should not be called directly by device drivers.
|
|
|
+ */
|
|
|
+int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
|
+{
|
|
|
+ return state > PCI_D0 ?
|
|
|
+ pci_platform_power_transition(dev, state) : -EINVAL;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(__pci_complete_power_transition);
|
|
|
+
|
|
|
/**
|
|
|
* pci_set_power_state - Set the power state of a PCI device
|
|
|
* @dev: PCI device to handle.
|
|
@@ -575,16 +622,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|
|
if (dev->current_state == state)
|
|
|
return 0;
|
|
|
|
|
|
- if (state == PCI_D0) {
|
|
|
- /*
|
|
|
- * Allow the platform to change the state, for example via ACPI
|
|
|
- * _PR0, _PS0 and some such, but do not trust it.
|
|
|
- */
|
|
|
- int ret = platform_pci_power_manageable(dev) ?
|
|
|
- platform_pci_set_power_state(dev, PCI_D0) : 0;
|
|
|
- if (!ret)
|
|
|
- pci_update_current_state(dev, PCI_D0);
|
|
|
- }
|
|
|
+ __pci_start_power_transition(dev, state);
|
|
|
+
|
|
|
/* This device is quirked not to be put into D3, so
|
|
|
don't put it in D3 */
|
|
|
if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
|
|
@@ -592,14 +631,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|
|
|
|
|
error = pci_raw_set_power_state(dev, state);
|
|
|
|
|
|
- if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
|
|
|
- /* Allow the platform to finalize the transition */
|
|
|
- int ret = platform_pci_set_power_state(dev, state);
|
|
|
- if (!ret) {
|
|
|
- pci_update_current_state(dev, state);
|
|
|
- error = 0;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!__pci_complete_power_transition(dev, state))
|
|
|
+ error = 0;
|
|
|
|
|
|
return error;
|
|
|
}
|