Merge branch 'acpi-pm' into fixes
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Sat, 23 Feb 2013 22:15:43 +0000 (23:15 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Sat, 23 Feb 2013 22:15:43 +0000 (23:15 +0100)
* acpi-pm:
  ACPI / PM: Take unusual configurations of power resources into account

drivers/acpi/internal.h
drivers/acpi/power.c
drivers/acpi/scan.c

index 79092328cf064b237c090690c29b64ec1a733063..4d77716ededdd50d16071b54b780d76ce113a580 100644 (file)
@@ -65,7 +65,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_power_wakeup_list_init(struct list_head *list, int *system_level);
 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 b820528a5fa3fa99a2c6628dd7aa0740776a402e..34f5ef11d427bd8f9acb4690b6c64b41d9c331b0 100644 (file)
@@ -73,6 +73,7 @@ struct acpi_power_resource {
        u32 system_level;
        u32 order;
        unsigned int ref_count;
+       bool wakeup_enabled;
        struct mutex resource_lock;
 };
 
@@ -272,11 +273,9 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
        return 0;
 }
 
-static int acpi_power_on(struct acpi_power_resource *resource)
+static int acpi_power_on_unlocked(struct acpi_power_resource *resource)
 {
-       int result = 0;;
-
-       mutex_lock(&resource->resource_lock);
+       int result = 0;
 
        if (resource->ref_count++) {
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@@ -293,9 +292,16 @@ static int acpi_power_on(struct acpi_power_resource *resource)
                                schedule_work(&dep->work);
                }
        }
+       return result;
+}
 
-       mutex_unlock(&resource->resource_lock);
+static int acpi_power_on(struct acpi_power_resource *resource)
+{
+       int result;
 
+       mutex_lock(&resource->resource_lock);
+       result = acpi_power_on_unlocked(resource);
+       mutex_unlock(&resource->resource_lock);
        return result;
 }
 
@@ -313,17 +319,15 @@ static int __acpi_power_off(struct acpi_power_resource *resource)
        return 0;
 }
 
-static int acpi_power_off(struct acpi_power_resource *resource)
+static int acpi_power_off_unlocked(struct acpi_power_resource *resource)
 {
        int result = 0;
 
-       mutex_lock(&resource->resource_lock);
-
        if (!resource->ref_count) {
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                                  "Power resource [%s] already off",
                                  resource->name));
-               goto unlock;
+               return 0;
        }
 
        if (--resource->ref_count) {
@@ -335,10 +339,16 @@ static int acpi_power_off(struct acpi_power_resource *resource)
                if (result)
                        resource->ref_count++;
        }
+       return result;
+}
 
- unlock:
-       mutex_unlock(&resource->resource_lock);
+static int acpi_power_off(struct acpi_power_resource *resource)
+{
+       int result;
 
+       mutex_lock(&resource->resource_lock);
+       result = acpi_power_off_unlocked(resource);
+       mutex_unlock(&resource->resource_lock);
        return result;
 }
 
@@ -521,18 +531,35 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
        }
 }
 
-int acpi_power_min_system_level(struct list_head *list)
+int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p)
 {
        struct acpi_power_resource_entry *entry;
        int system_level = 5;
 
        list_for_each_entry(entry, list, node) {
                struct acpi_power_resource *resource = entry->resource;
+               acpi_handle handle = resource->device.handle;
+               int result;
+               int state;
 
+               mutex_lock(&resource->resource_lock);
+
+               result = acpi_power_get_state(handle, &state);
+               if (result) {
+                       mutex_unlock(&resource->resource_lock);
+                       return result;
+               }
+               if (state == ACPI_POWER_RESOURCE_STATE_ON) {
+                       resource->ref_count++;
+                       resource->wakeup_enabled = true;
+               }
                if (system_level > resource->system_level)
                        system_level = resource->system_level;
+
+               mutex_unlock(&resource->resource_lock);
        }
-       return system_level;
+       *system_level_p = system_level;
+       return 0;
 }
 
 /* --------------------------------------------------------------------------
@@ -610,6 +637,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev,
  */
 int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
 {
+       struct acpi_power_resource_entry *entry;
        int err = 0;
 
        if (!dev || !dev->wakeup.flags.valid)
@@ -620,17 +648,31 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
        if (dev->wakeup.prepare_count++)
                goto out;
 
-       err = acpi_power_on_list(&dev->wakeup.resources);
-       if (err) {
-               dev_err(&dev->dev, "Cannot turn wakeup power resources on\n");
-               dev->wakeup.flags.valid = 0;
-       } else {
-               /*
-                * Passing 3 as the third argument below means the device may be
-                * put into arbitrary power state afterward.
-                */
-               err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
+       list_for_each_entry(entry, &dev->wakeup.resources, node) {
+               struct acpi_power_resource *resource = entry->resource;
+
+               mutex_lock(&resource->resource_lock);
+
+               if (!resource->wakeup_enabled) {
+                       err = acpi_power_on_unlocked(resource);
+                       if (!err)
+                               resource->wakeup_enabled = true;
+               }
+
+               mutex_unlock(&resource->resource_lock);
+
+               if (err) {
+                       dev_err(&dev->dev,
+                               "Cannot turn wakeup power resources on\n");
+                       dev->wakeup.flags.valid = 0;
+                       goto out;
+               }
        }
+       /*
+        * Passing 3 as the third argument below means the device may be
+        * put into arbitrary power state afterward.
+        */
+       err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
        if (err)
                dev->wakeup.prepare_count = 0;
 
@@ -647,6 +689,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
  */
 int acpi_disable_wakeup_device_power(struct acpi_device *dev)
 {
+       struct acpi_power_resource_entry *entry;
        int err = 0;
 
        if (!dev || !dev->wakeup.flags.valid)
@@ -668,10 +711,25 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
        if (err)
                goto out;
 
-       err = acpi_power_off_list(&dev->wakeup.resources);
-       if (err) {
-               dev_err(&dev->dev, "Cannot turn wakeup power resources off\n");
-               dev->wakeup.flags.valid = 0;
+       list_for_each_entry(entry, &dev->wakeup.resources, node) {
+               struct acpi_power_resource *resource = entry->resource;
+
+               mutex_lock(&resource->resource_lock);
+
+               if (resource->wakeup_enabled) {
+                       err = acpi_power_off_unlocked(resource);
+                       if (!err)
+                               resource->wakeup_enabled = false;
+               }
+
+               mutex_unlock(&resource->resource_lock);
+
+               if (err) {
+                       dev_err(&dev->dev,
+                               "Cannot turn wakeup power resources off\n");
+                       dev->wakeup.flags.valid = 0;
+                       break;
+               }
        }
 
  out:
index daee7497efd30137f2dddef2497f9968555d4e26..1c6104c942c5ba5a8f6b556892e71a587a819e70 100644 (file)
@@ -1002,7 +1002,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",