summaryrefslogtreecommitdiff
path: root/drivers/acpi/bus.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-05-20 13:58:00 +0200
committerRafael J. Wysocki <rjw@sisk.pl>2012-05-29 21:20:24 +0200
commit38c92fff988d518fe80dc23d0d44d66bd7e47ddd (patch)
treec18cc469f47bf0e79f3617c59175978e60f31878 /drivers/acpi/bus.c
parent63a1a765dffb1e59d82c7948638e56d5f4f2e3a1 (diff)
downloadlinux-3.10-38c92fff988d518fe80dc23d0d44d66bd7e47ddd.tar.gz
linux-3.10-38c92fff988d518fe80dc23d0d44d66bd7e47ddd.tar.bz2
linux-3.10-38c92fff988d518fe80dc23d0d44d66bd7e47ddd.zip
ACPI / PM: Make __acpi_bus_get_power() cover D3cold correctly
After recent changes of the ACPI device power states definitions, if power resources are not used for the device's power management, the state returned by __acpi_bus_get_power() cannot exceed D3hot, because the return values of _PSC are 0 through 3. However, if the _PR3 method is not present for the device and _PS3 returns 3, we have to assume that the device is in D3cold, so the value returned by __acpi_bus_get_power() in that case should be 4. Similarly, acpi_power_get_inferred_state() should take the power resources for the D3hot state into account in general, so that it can return 3 if those resources are "on" or 4 (D3cold) otherwise. Fix the the above two issues and make sure that if both _PSC and _PR3 are present for the device, the power resources listed by _PR3 will be used to determine if the number 3 returned by _PSC is meant to represent D3cold or D3hot. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/acpi/bus.c')
-rw-r--r--drivers/acpi/bus.c51
1 files changed, 29 insertions, 22 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a41be56c1cc..adceafda9c1 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -202,37 +202,44 @@ static const char *state_string(int state)
static int __acpi_bus_get_power(struct acpi_device *device, int *state)
{
- int result = 0;
- acpi_status status = 0;
- unsigned long long psc = 0;
+ int result = ACPI_STATE_UNKNOWN;
if (!device || !state)
return -EINVAL;
- *state = ACPI_STATE_UNKNOWN;
-
- if (device->flags.power_manageable) {
- /*
- * Get the device's power state either directly (via _PSC) or
- * indirectly (via power resources).
- */
- if (device->power.flags.power_resources) {
- result = acpi_power_get_inferred_state(device, state);
- if (result)
- return result;
- } else if (device->power.flags.explicit_get) {
- status = acpi_evaluate_integer(device->handle, "_PSC",
- NULL, &psc);
- if (ACPI_FAILURE(status))
- return -ENODEV;
- *state = (int)psc;
- }
- } else {
+ if (!device->flags.power_manageable) {
/* TBD: Non-recursive algorithm for walking up hierarchy. */
*state = device->parent ?
device->parent->power.state : ACPI_STATE_D0;
+ goto out;
+ }
+
+ /*
+ * Get the device's power state either directly (via _PSC) or
+ * indirectly (via power resources).
+ */
+ if (device->power.flags.explicit_get) {
+ unsigned long long psc;
+ acpi_status status = acpi_evaluate_integer(device->handle,
+ "_PSC", NULL, &psc);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ result = psc;
+ }
+ /* The test below covers ACPI_STATE_UNKNOWN too. */
+ if (result <= ACPI_STATE_D2) {
+ ; /* Do nothing. */
+ } else if (device->power.flags.power_resources) {
+ int error = acpi_power_get_inferred_state(device, &result);
+ if (error)
+ return error;
+ } else if (result == ACPI_STATE_D3_HOT) {
+ result = ACPI_STATE_D3;
}
+ *state = result;
+ out:
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
device->pnp.bus_id, state_string(*state)));