|
@@ -587,7 +587,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|
dev_info(&dev->dev, "Refused to change power state, "
|
|
dev_info(&dev->dev, "Refused to change power state, "
|
|
"currently in D%d\n", dev->current_state);
|
|
"currently in D%d\n", dev->current_state);
|
|
|
|
|
|
- /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
|
|
|
|
|
|
+ /*
|
|
|
|
+ * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
|
|
* INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
|
|
* INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
|
|
* from D3hot to D0 _may_ perform an internal reset, thereby
|
|
* from D3hot to D0 _may_ perform an internal reset, thereby
|
|
* going to "D0 Uninitialized" rather than "D0 Initialized".
|
|
* going to "D0 Uninitialized" rather than "D0 Initialized".
|
|
@@ -619,6 +620,16 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
|
|
if (dev->pm_cap) {
|
|
if (dev->pm_cap) {
|
|
u16 pmcsr;
|
|
u16 pmcsr;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Configuration space is not accessible for device in
|
|
|
|
+ * D3cold, so just keep or set D3cold for safety
|
|
|
|
+ */
|
|
|
|
+ if (dev->current_state == PCI_D3cold)
|
|
|
|
+ return;
|
|
|
|
+ if (state == PCI_D3cold) {
|
|
|
|
+ dev->current_state = PCI_D3cold;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
|
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
|
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
|
|
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
|
|
} else {
|
|
} else {
|
|
@@ -659,8 +670,50 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
*/
|
|
*/
|
|
static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
{
|
|
{
|
|
- if (state == PCI_D0)
|
|
|
|
|
|
+ if (state == PCI_D0) {
|
|
pci_platform_power_transition(dev, PCI_D0);
|
|
pci_platform_power_transition(dev, PCI_D0);
|
|
|
|
+ /*
|
|
|
|
+ * Mandatory power management transition delays, see
|
|
|
|
+ * PCI Express Base Specification Revision 2.0 Section
|
|
|
|
+ * 6.6.1: Conventional Reset. Do not delay for
|
|
|
|
+ * devices powered on/off by corresponding bridge,
|
|
|
|
+ * because have already delayed for the bridge.
|
|
|
|
+ */
|
|
|
|
+ if (dev->runtime_d3cold) {
|
|
|
|
+ msleep(dev->d3cold_delay);
|
|
|
|
+ /*
|
|
|
|
+ * When powering on a bridge from D3cold, the
|
|
|
|
+ * whole hierarchy may be powered on into
|
|
|
|
+ * D0uninitialized state, resume them to give
|
|
|
|
+ * them a chance to suspend again
|
|
|
|
+ */
|
|
|
|
+ pci_wakeup_bus(dev->subordinate);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * __pci_dev_set_current_state - Set current state of a PCI device
|
|
|
|
+ * @dev: Device to handle
|
|
|
|
+ * @data: pointer to state to be set
|
|
|
|
+ */
|
|
|
|
+static int __pci_dev_set_current_state(struct pci_dev *dev, void *data)
|
|
|
|
+{
|
|
|
|
+ pci_power_t state = *(pci_power_t *)data;
|
|
|
|
+
|
|
|
|
+ dev->current_state = state;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * __pci_bus_set_current_state - Walk given bus and set current state of devices
|
|
|
|
+ * @bus: Top bus of the subtree to walk.
|
|
|
|
+ * @state: state to be set
|
|
|
|
+ */
|
|
|
|
+static void __pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state)
|
|
|
|
+{
|
|
|
|
+ if (bus)
|
|
|
|
+ pci_walk_bus(bus, __pci_dev_set_current_state, &state);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -672,8 +725,15 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
*/
|
|
*/
|
|
int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state)
|
|
{
|
|
{
|
|
- return state >= PCI_D0 ?
|
|
|
|
- pci_platform_power_transition(dev, state) : -EINVAL;
|
|
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (state < PCI_D0)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ ret = pci_platform_power_transition(dev, state);
|
|
|
|
+ /* Power off the bridge may power off the whole hierarchy */
|
|
|
|
+ if (!ret && state == PCI_D3cold)
|
|
|
|
+ __pci_bus_set_current_state(dev->subordinate, PCI_D3cold);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(__pci_complete_power_transition);
|
|
EXPORT_SYMBOL_GPL(__pci_complete_power_transition);
|
|
|
|
|
|
@@ -697,8 +757,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|
int error;
|
|
int error;
|
|
|
|
|
|
/* bound the state we're entering */
|
|
/* bound the state we're entering */
|
|
- if (state > PCI_D3hot)
|
|
|
|
- state = PCI_D3hot;
|
|
|
|
|
|
+ if (state > PCI_D3cold)
|
|
|
|
+ state = PCI_D3cold;
|
|
else if (state < PCI_D0)
|
|
else if (state < PCI_D0)
|
|
state = PCI_D0;
|
|
state = PCI_D0;
|
|
else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
|
|
else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
|
|
@@ -713,10 +773,15 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|
|
|
|
|
/* This device is quirked not to be put into D3, so
|
|
/* This device is quirked not to be put into D3, so
|
|
don't put it in D3 */
|
|
don't put it in D3 */
|
|
- if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
|
|
|
|
|
|
+ if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- error = pci_raw_set_power_state(dev, state);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * To put device in D3cold, we put device into D3hot in native
|
|
|
|
+ * way, then put device into D3cold with platform ops
|
|
|
|
+ */
|
|
|
|
+ error = pci_raw_set_power_state(dev, state > PCI_D3hot ?
|
|
|
|
+ PCI_D3hot : state);
|
|
|
|
|
|
if (!__pci_complete_power_transition(dev, state))
|
|
if (!__pci_complete_power_transition(dev, state))
|
|
error = 0;
|
|
error = 0;
|
|
@@ -1459,6 +1524,28 @@ void pci_pme_wakeup_bus(struct pci_bus *bus)
|
|
pci_walk_bus(bus, pci_pme_wakeup, (void *)true);
|
|
pci_walk_bus(bus, pci_pme_wakeup, (void *)true);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * pci_wakeup - Wake up a PCI device
|
|
|
|
+ * @dev: Device to handle.
|
|
|
|
+ * @ign: ignored parameter
|
|
|
|
+ */
|
|
|
|
+static int pci_wakeup(struct pci_dev *pci_dev, void *ign)
|
|
|
|
+{
|
|
|
|
+ pci_wakeup_event(pci_dev);
|
|
|
|
+ pm_request_resume(&pci_dev->dev);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * pci_wakeup_bus - Walk given bus and wake up devices on it
|
|
|
|
+ * @bus: Top bus of the subtree to walk.
|
|
|
|
+ */
|
|
|
|
+void pci_wakeup_bus(struct pci_bus *bus)
|
|
|
|
+{
|
|
|
|
+ if (bus)
|
|
|
|
+ pci_walk_bus(bus, pci_wakeup, NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* pci_pme_capable - check the capability of PCI device to generate PME#
|
|
* pci_pme_capable - check the capability of PCI device to generate PME#
|
|
* @dev: PCI device to handle.
|
|
* @dev: PCI device to handle.
|
|
@@ -1480,6 +1567,16 @@ static void pci_pme_list_scan(struct work_struct *work)
|
|
if (!list_empty(&pci_pme_list)) {
|
|
if (!list_empty(&pci_pme_list)) {
|
|
list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
|
|
list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
|
|
if (pme_dev->dev->pme_poll) {
|
|
if (pme_dev->dev->pme_poll) {
|
|
|
|
+ struct pci_dev *bridge;
|
|
|
|
+
|
|
|
|
+ bridge = pme_dev->dev->bus->self;
|
|
|
|
+ /*
|
|
|
|
+ * If bridge is in low power state, the
|
|
|
|
+ * configuration space of subordinate devices
|
|
|
|
+ * may be not accessible
|
|
|
|
+ */
|
|
|
|
+ if (bridge && bridge->current_state != PCI_D0)
|
|
|
|
+ continue;
|
|
pci_pme_wakeup(pme_dev->dev, NULL);
|
|
pci_pme_wakeup(pme_dev->dev, NULL);
|
|
} else {
|
|
} else {
|
|
list_del(&pme_dev->list);
|
|
list_del(&pme_dev->list);
|
|
@@ -1706,6 +1803,10 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
|
|
if (target_state == PCI_POWER_ERROR)
|
|
if (target_state == PCI_POWER_ERROR)
|
|
return -EIO;
|
|
return -EIO;
|
|
|
|
|
|
|
|
+ /* D3cold during system suspend/hibernate is not supported */
|
|
|
|
+ if (target_state > PCI_D3hot)
|
|
|
|
+ target_state = PCI_D3hot;
|
|
|
|
+
|
|
pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev));
|
|
pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev));
|
|
|
|
|
|
error = pci_set_power_state(dev, target_state);
|
|
error = pci_set_power_state(dev, target_state);
|
|
@@ -1743,12 +1844,16 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
|
|
if (target_state == PCI_POWER_ERROR)
|
|
if (target_state == PCI_POWER_ERROR)
|
|
return -EIO;
|
|
return -EIO;
|
|
|
|
|
|
|
|
+ dev->runtime_d3cold = target_state == PCI_D3cold;
|
|
|
|
+
|
|
__pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev));
|
|
__pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev));
|
|
|
|
|
|
error = pci_set_power_state(dev, target_state);
|
|
error = pci_set_power_state(dev, target_state);
|
|
|
|
|
|
- if (error)
|
|
|
|
|
|
+ if (error) {
|
|
__pci_enable_wake(dev, target_state, true, false);
|
|
__pci_enable_wake(dev, target_state, true, false);
|
|
|
|
+ dev->runtime_d3cold = false;
|
|
|
|
+ }
|
|
|
|
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
@@ -1818,6 +1923,7 @@ void pci_pm_init(struct pci_dev *dev)
|
|
|
|
|
|
dev->pm_cap = pm;
|
|
dev->pm_cap = pm;
|
|
dev->d3_delay = PCI_PM_D3_WAIT;
|
|
dev->d3_delay = PCI_PM_D3_WAIT;
|
|
|
|
+ dev->d3cold_delay = PCI_PM_D3COLD_WAIT;
|
|
|
|
|
|
dev->d1_support = false;
|
|
dev->d1_support = false;
|
|
dev->d2_support = false;
|
|
dev->d2_support = false;
|