From b5d667eb392ed901fc7ae76869c7a130559e193c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 23 Feb 2013 23:15:21 +0100 Subject: ACPI / PM: Take unusual configurations of power resources into account Commit d2e5f0c (ACPI / PCI: Rework the setup and cleanup of device wakeup) moved the initial disabling of system wakeup for PCI devices into a place where it can actually work and that exposed a hidden old issue with crap^Wunusual system designs where the same power resources are used for both wakeup power and device power control at run time. Namely, say there is one power resource such that the ACPI power state D0 of a PCI device depends on that power resource (i.e. the device is in D0 when that power resource is "on") and it is used as a wakeup power resource for the same device. Then, calling acpi_pci_sleep_wake(pci_dev, false) for the device in question will cause the reference counter of that power resource to drop to 0, which in turn will cause it to be turned off. As a result, the device will go into D3cold at that point, although it should have stayed in D0. As it turns out, that happens to USB controllers on some laptops and USB becomes unusable on those machines as a result, which is a major regression from v3.8. To fix this problem, (1) increment the reference counters of wakup power resources during their initialization if they are "on" initially, (2) prevent acpi_disable_wakeup_device_power() from decrementing the reference counters of wakeup power resources that were not enabled for wakeup power previously, and (3) prevent acpi_enable_wakeup_device_power() from incrementing the reference counters of wakeup power resources that already are enabled for wakeup power. In addition to that, if it is impossible to determine the initial states of wakeup power resources, avoid enabling wakeup for devices whose wakeup power depends on those power resources. Reported-by: Dave Jones Reported-by: Fabio Baltieri Tested-by: Fabio Baltieri Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/acpi/scan.c') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f75f25c2e455..560b05566f3b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1004,7 +1004,14 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, if (!list_empty(&wakeup->resources)) { int sleep_state; - sleep_state = acpi_power_min_system_level(&wakeup->resources); + err = acpi_power_wakeup_list_init(&wakeup->resources, + &sleep_state); + if (err) { + acpi_handle_warn(handle, "Retrieving current states " + "of wakeup power resources failed\n"); + acpi_power_resources_list_free(&wakeup->resources); + goto out; + } if (sleep_state < wakeup->sleep_state) { acpi_handle_warn(handle, "Overriding _PRW sleep state " "(S%d) by S%d from power resources\n", -- cgit v1.2.3