ACPI: Use system level attribute of wakeup power resources
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 17 Jan 2013 13:11:07 +0000 (14:11 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 17 Jan 2013 13:11:07 +0000 (14:11 +0100)
The system level attribute of ACPI power resources is the lowest
system sleep level (S0, S2 etc.) in which the given resource can be
"on" (ACPI 5.0, Section 7.1).  On the other hand, wakeup power
resources have to be "on" for devices depending on them to be able to
signal wakeup.  Therefore devices cannot wake up the system from
sleep states higher than the minimum of the system level attributes
of their wakeup power resources.

Use the wakeup power resources' system level values to get the
deepest system sleep state (highest system sleep level) the given
device can wake up the system from.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/internal.h
drivers/acpi/power.c
drivers/acpi/scan.c

index 8a6c67c9da4209e648c87822bb99c34e8676a403..07f61dbd81360a6fb42160a9505e451b306c3e78 100644 (file)
@@ -55,6 +55,7 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
                                 struct list_head *list);
 int acpi_add_power_resource(acpi_handle handle);
 void acpi_power_add_remove_device(struct acpi_device *adev, bool add);
+int acpi_power_min_system_level(struct list_head *list);
 int acpi_device_sleep_wake(struct acpi_device *dev,
                            int enable, int sleep_state, int dev_state);
 int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
index 1600f753fafed4861fcb5d51fbe15df934d0baad..089a7c39348fe676429a64afec817539811f8d65 100644 (file)
@@ -429,6 +429,20 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
        }
 }
 
+int acpi_power_min_system_level(struct list_head *list)
+{
+       struct acpi_power_resource_entry *entry;
+       int system_level = 5;
+
+       list_for_each_entry(entry, list, node) {
+               struct acpi_power_resource *resource = entry->resource;
+
+               if (system_level > resource->system_level)
+                       system_level = resource->system_level;
+       }
+       return system_level;
+}
+
 /* --------------------------------------------------------------------------
                              Device Power Management
    -------------------------------------------------------------------------- */
index 0b6a6b4febd69f8c06a3d3ac3e96fb04388dcb60..1fc57a349a3cf3164c64a348ff3fac17d9ccf44b 100644 (file)
@@ -950,6 +950,17 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
        if (err)
                goto out;
 
+       if (!list_empty(&wakeup->resources)) {
+               int sleep_state;
+
+               sleep_state = acpi_power_min_system_level(&wakeup->resources);
+               if (sleep_state < wakeup->sleep_state) {
+                       acpi_handle_warn(handle, "Overriding _PRW sleep state "
+                                        "(S%d) by S%d from power resources\n",
+                                        (int)wakeup->sleep_state, sleep_state);
+                       wakeup->sleep_state = sleep_state;
+               }
+       }
        acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number);
 
  out: