aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2012-05-17 18:39:35 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-17 19:16:16 -0400
commit5c7dd710f691d1b44c39e32d2f05b4286ff51f99 (patch)
tree7c319291d09980496d2b01dcbb6d9138db9477be /drivers/acpi/power.c
parenteea036475df8995e5b87cd6b8c8e682e071159cd (diff)
ACPI / PCI / PM: Fix device PM regression related to D3hot/D3cold
Commit 1cc0c998fdf2 ("ACPI: Fix D3hot v D3cold confusion") introduced a bug in __acpi_bus_set_power() and changed the behavior of acpi_pci_set_power_state() in such a way that it generally doesn't work as expected if PCI_D3hot is passed to it as the second argument. First off, if ACPI_STATE_D3 (equal to ACPI_STATE_D3_COLD) is passed to __acpi_bus_set_power() and the explicit_set flag is set for the D3cold state, the function will try to execute AML method called "_PS4", which doesn't exist. Fix this by adding a check to ensure that the name of the AML method to execute for transitions to ACPI_STATE_D3_COLD is correct in __acpi_bus_set_power(). Also make sure that the explicit_set flag for ACPI_STATE_D3_COLD will be set if _PS3 is present and modify acpi_power_transition() to avoid accessing power resources for ACPI_STATE_D3_COLD, because they don't exist. Second, if PCI_D3hot is passed to acpi_pci_set_power_state() as the target state, the function will request a transition to ACPI_STATE_D3_HOT instead of ACPI_STATE_D3. However, ACPI_STATE_D3_HOT is now only marked as supported if the _PR3 AML method is defined for the given device, which is rare. This causes problems to happen on systems where devices were successfully put into ACPI D3 by pci_set_power_state(PCI_D3hot) which doesn't work now. In particular, some unused graphics adapters are not turned off as a result. To fix this issue restore the old behavior of acpi_pci_set_power_state(), which is to request a transition to ACPI_STATE_D3 (equal to ACPI_STATE_D3_COLD) if either PCI_D3hot or PCI_D3cold is passed to it as the argument. This approach is not ideal, because generally power should not be removed from devices if PCI_D3hot is the target power state, but since this behavior is relied on, we have no choice but to restore it at the moment and spend more time on designing a better solution in the future. References: https://bugzilla.kernel.org/show_bug.cgi?id=43228 Reported-by: rocko <rockorequin@hotmail.com> Reported-by: Cristian Rodríguez <crrodriguez@opensuse.org> Reported-and-tested-by: Peter <lekensteyn@gmail.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r--drivers/acpi/power.c9
1 files changed, 6 insertions, 3 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 330bb4d75852..0500f719f63e 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -660,7 +660,7 @@ int acpi_power_on_resources(struct acpi_device *device, int state)
660 660
661int acpi_power_transition(struct acpi_device *device, int state) 661int acpi_power_transition(struct acpi_device *device, int state)
662{ 662{
663 int result; 663 int result = 0;
664 664
665 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) 665 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
666 return -EINVAL; 666 return -EINVAL;
@@ -679,8 +679,11 @@ int acpi_power_transition(struct acpi_device *device, int state)
679 * (e.g. so the device doesn't lose power while transitioning). Then, 679 * (e.g. so the device doesn't lose power while transitioning). Then,
680 * we dereference all power resources used in the current list. 680 * we dereference all power resources used in the current list.
681 */ 681 */
682 result = acpi_power_on_list(&device->power.states[state].resources); 682 if (state < ACPI_STATE_D3_COLD)
683 if (!result) 683 result = acpi_power_on_list(
684 &device->power.states[state].resources);
685
686 if (!result && device->power.state < ACPI_STATE_D3_COLD)
684 acpi_power_off_list( 687 acpi_power_off_list(
685 &device->power.states[device->power.state].resources); 688 &device->power.states[device->power.state].resources);
686 689