Skip to content

Commit

Permalink
PM / ACPI: Take device PM QoS flags into account
Browse files Browse the repository at this point in the history
Make ACPI power management routines and PCI power management
routines depending on ACPI take device PM QoS flags into account
when deciding what power state to put the device into.

In particular, after this change acpi_pm_device_sleep_state() will
not return ACPI_STATE_D3_COLD as the deepest available low-power
state if PM_QOS_FLAG_NO_POWER_OFF is requested for the device and it
will not require remote wakeup to work for the device in the returned
low-power state if there is at least one PM QoS flags request for the
device, but PM_QOS_FLAG_REMOTE_WAKEUP is not requested for it.

Accordingly, acpi_pci_set_power_state() will refuse to put the
device into D3cold if PM_QOS_FLAG_NO_POWER_OFF is requested for it.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Huang Ying <ying.huang@intel.com>
  • Loading branch information
Rafael J. Wysocki committed Oct 24, 2012
1 parent 34b1f76 commit 8b713a8
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 5 deletions.
21 changes: 17 additions & 4 deletions drivers/acpi/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>

#include <asm/io.h>

Expand Down Expand Up @@ -711,13 +712,21 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
struct acpi_device *adev;
char acpi_method[] = "_SxD";
unsigned long long d_min, d_max;
bool wakeup = false;

if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3)
return -EINVAL;
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) {
printk(KERN_DEBUG "ACPI handle has no context!\n");
return -ENODEV;
}
if (d_max_in > ACPI_STATE_D3_HOT) {
enum pm_qos_flags_status stat;

stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF);
if (stat == PM_QOS_FLAGS_ALL)
d_max_in = ACPI_STATE_D3_HOT;
}

acpi_method[2] = '0' + acpi_target_sleep_state;
/*
Expand All @@ -737,8 +746,14 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
* NOTE: We rely on acpi_evaluate_integer() not clobbering the integer
* provided -- that's our fault recovery, we ignore retval.
*/
if (acpi_target_sleep_state > ACPI_STATE_S0)
if (acpi_target_sleep_state > ACPI_STATE_S0) {
acpi_evaluate_integer(handle, acpi_method, NULL, &d_min);
wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid
&& adev->wakeup.sleep_state >= acpi_target_sleep_state;
} else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) !=
PM_QOS_FLAGS_NONE) {
wakeup = adev->wakeup.flags.valid;
}

/*
* If _PRW says we can wake up the system from the target sleep state,
Expand All @@ -747,9 +762,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
* (ACPI 3.x), it should return the maximum (lowest power) D-state that
* can wake the system. _S0W may be valid, too.
*/
if (acpi_target_sleep_state == ACPI_STATE_S0 ||
(device_may_wakeup(dev) && adev->wakeup.flags.valid &&
adev->wakeup.sleep_state >= acpi_target_sleep_state)) {
if (wakeup) {
acpi_status status;

acpi_method[3] = 'W';
Expand Down
8 changes: 7 additions & 1 deletion drivers/pci/pci-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <linux/pci-acpi.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include "pci.h"

static DEFINE_MUTEX(pci_acpi_pm_notify_mtx);
Expand Down Expand Up @@ -257,11 +258,16 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
return -ENODEV;

switch (state) {
case PCI_D3cold:
if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) ==
PM_QOS_FLAGS_ALL) {
error = -EBUSY;
break;
}
case PCI_D0:
case PCI_D1:
case PCI_D2:
case PCI_D3hot:
case PCI_D3cold:
error = acpi_bus_set_power(handle, state_conv[state]);
}

Expand Down

0 comments on commit 8b713a8

Please sign in to comment.