summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-01-01 23:38:28 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-01-16 06:59:55 -0800
commit3dcf157fe88d1dba7d24da01458bf025b32f5534 (patch)
treef1b0a78a6dccd21a0b577f76258c388ac3eca5c4
parentde42aa3d478c813a02ef0413a2ebbf5d9c78a14b (diff)
ACPI / PM: Fix PM initialization for devices that are not present
commit 1b1f3e1699a9886f1070f94171097ab4ccdbfc95 upstream. If an ACPI device object whose _STA returns 0 (not present and not functional) has _PR0 or _PS0, its power_manageable flag will be set and acpi_bus_init_power() will return 0 for it. Consequently, if such a device object is passed to the ACPI device PM functions, they will attempt to carry out the requested operation on the device, although they should not do that for devices that are not present. To fix that problem make acpi_bus_init_power() return an error code for devices that are not present which will cause power_manageable to be cleared for them as appropriate in acpi_bus_get_power_flags(). However, the lists of power resources should not be freed for the device in that case, so modify acpi_bus_get_power_flags() to keep those lists even if acpi_bus_init_power() returns an error. Accordingly, when deciding whether or not the lists of power resources need to be freed, acpi_free_power_resources_lists() should check the power.flags.power_resources flag instead of flags.power_manageable, so make that change too. Furthermore, if acpi_bus_attach() sees that flags.initialized is unset for the given device, it should reset the power management settings of the device and re-initialize them from scratch instead of relying on the previous settings (the device may have appeared after being not present previously, for example), so make it use the 'valid' flag of the D0 power state as the initial value of flags.power_manageable for it and call acpi_bus_init_power() to discover its current power state. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/acpi/device_pm.c2
-rw-r--r--drivers/acpi/scan.c13
2 files changed, 9 insertions, 6 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 7db193160766..93b71420a046 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -257,7 +257,7 @@ int acpi_bus_init_power(struct acpi_device *device)
device->power.state = ACPI_STATE_UNKNOWN;
if (!acpi_device_is_present(device))
- return 0;
+ return -ENXIO;
result = acpi_device_get_power(device, &state);
if (result)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 0476e90b2091..c9ea3dfb4974 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -909,7 +909,7 @@ static void acpi_free_power_resources_lists(struct acpi_device *device)
if (device->wakeup.flags.valid)
acpi_power_resources_list_free(&device->wakeup.resources);
- if (!device->flags.power_manageable)
+ if (!device->power.flags.power_resources)
return;
for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
@@ -1631,10 +1631,8 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
device->power.flags.power_resources)
device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1;
- if (acpi_bus_init_power(device)) {
- acpi_free_power_resources_lists(device);
+ if (acpi_bus_init_power(device))
device->flags.power_manageable = 0;
- }
}
static void acpi_bus_get_flags(struct acpi_device *device)
@@ -2202,13 +2200,18 @@ static void acpi_bus_attach(struct acpi_device *device)
/* Skip devices that are not present. */
if (!acpi_device_is_present(device)) {
device->flags.visited = false;
+ device->flags.power_manageable = 0;
return;
}
if (device->handler)
goto ok;
if (!device->flags.initialized) {
- acpi_bus_update_power(device, NULL);
+ device->flags.power_manageable =
+ device->power.states[ACPI_STATE_D0].flags.valid;
+ if (acpi_bus_init_power(device))
+ device->flags.power_manageable = 0;
+
device->flags.initialized = true;
}
device->flags.visited = false;