Commit 3dcf157fe88d1dba7d24da01458bf025b32f5534

Authored by Rafael J. Wysocki
Committed by Greg Kroah-Hartman
1 parent de42aa3d47

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>

Showing 2 changed files with 9 additions and 6 deletions Side-by-side Diff

drivers/acpi/device_pm.c
... ... @@ -257,7 +257,7 @@
257 257  
258 258 device->power.state = ACPI_STATE_UNKNOWN;
259 259 if (!acpi_device_is_present(device))
260   - return 0;
  260 + return -ENXIO;
261 261  
262 262 result = acpi_device_get_power(device, &state);
263 263 if (result)
... ... @@ -909,7 +909,7 @@
909 909 if (device->wakeup.flags.valid)
910 910 acpi_power_resources_list_free(&device->wakeup.resources);
911 911  
912   - if (!device->flags.power_manageable)
  912 + if (!device->power.flags.power_resources)
913 913 return;
914 914  
915 915 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
916 916  
... ... @@ -1631,10 +1631,8 @@
1631 1631 device->power.flags.power_resources)
1632 1632 device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1;
1633 1633  
1634   - if (acpi_bus_init_power(device)) {
1635   - acpi_free_power_resources_lists(device);
  1634 + if (acpi_bus_init_power(device))
1636 1635 device->flags.power_manageable = 0;
1637   - }
1638 1636 }
1639 1637  
1640 1638 static void acpi_bus_get_flags(struct acpi_device *device)
1641 1639  
... ... @@ -2202,13 +2200,18 @@
2202 2200 /* Skip devices that are not present. */
2203 2201 if (!acpi_device_is_present(device)) {
2204 2202 device->flags.visited = false;
  2203 + device->flags.power_manageable = 0;
2205 2204 return;
2206 2205 }
2207 2206 if (device->handler)
2208 2207 goto ok;
2209 2208  
2210 2209 if (!device->flags.initialized) {
2211   - acpi_bus_update_power(device, NULL);
  2210 + device->flags.power_manageable =
  2211 + device->power.states[ACPI_STATE_D0].flags.valid;
  2212 + if (acpi_bus_init_power(device))
  2213 + device->flags.power_manageable = 0;
  2214 +
2212 2215 device->flags.initialized = true;
2213 2216 }
2214 2217 device->flags.visited = false;