| From 91bdad0b6237c25a7bf8fd4604d0cc64a2005a23 Mon Sep 17 00:00:00 2001 |
| From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> |
| Date: Thu, 4 Jul 2013 13:22:11 +0200 |
| Subject: ACPI / PM: Fix corner case in acpi_bus_update_power() |
| |
| From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> |
| |
| commit 91bdad0b6237c25a7bf8fd4604d0cc64a2005a23 upstream. |
| |
| The role of acpi_bus_update_power() is to update the given ACPI |
| device object's power.state field to reflect the current physical |
| state of the device (as inferred from the configuration of power |
| resources and _PSC, if available). For this purpose it calls |
| acpi_device_set_power() that should update the power resources' |
| reference counters and set power.state as appropriate. However, |
| that doesn't work if the "new" state is D1, D2 or D3hot and the |
| the current value of power.state means D3cold, because in that |
| case acpi_device_set_power() will refuse to transition the device |
| from D3cold to non-D0. |
| |
| To address this problem, make acpi_bus_update_power() call |
| acpi_power_transition() directly to update the power resources' |
| reference counters and only use acpi_device_set_power() to put |
| the device into D0 if the current physical state of it cannot |
| be determined. |
| |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/acpi/device_pm.c | 23 ++++++++++++++++++----- |
| 1 file changed, 18 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/acpi/device_pm.c |
| +++ b/drivers/acpi/device_pm.c |
| @@ -295,14 +295,27 @@ int acpi_bus_update_power(acpi_handle ha |
| if (result) |
| return result; |
| |
| - if (state == ACPI_STATE_UNKNOWN) |
| + if (state == ACPI_STATE_UNKNOWN) { |
| state = ACPI_STATE_D0; |
| - |
| - result = acpi_device_set_power(device, state); |
| - if (!result && state_p) |
| + result = acpi_device_set_power(device, state); |
| + if (result) |
| + return result; |
| + } else { |
| + if (device->power.flags.power_resources) { |
| + /* |
| + * We don't need to really switch the state, bu we need |
| + * to update the power resources' reference counters. |
| + */ |
| + result = acpi_power_transition(device, state); |
| + if (result) |
| + return result; |
| + } |
| + device->power.state = state; |
| + } |
| + if (state_p) |
| *state_p = state; |
| |
| - return result; |
| + return 0; |
| } |
| EXPORT_SYMBOL_GPL(acpi_bus_update_power); |
| |