diff options
Diffstat (limited to 'drivers/acpi/power.c')
| -rw-r--r-- | drivers/acpi/power.c | 292 |
1 files changed, 135 insertions, 157 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index b820528a5fa..e0bcfb642b5 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -42,13 +42,10 @@ #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/sysfs.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h> #include "sleep.h" #include "internal.h" -#define PREFIX "ACPI: " - #define _COMPONENT ACPI_POWER_COMPONENT ACPI_MODULE_NAME("power"); #define ACPI_POWER_CLASS "power_resource" @@ -59,20 +56,14 @@ ACPI_MODULE_NAME("power"); #define ACPI_POWER_RESOURCE_STATE_ON 0x01 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF -struct acpi_power_dependent_device { - struct list_head node; - struct acpi_device *adev; - struct work_struct work; -}; - struct acpi_power_resource { struct acpi_device device; struct list_head list_node; - struct list_head dependent; char *name; u32 system_level; u32 order; unsigned int ref_count; + bool wakeup_enabled; struct mutex resource_lock; }; @@ -232,32 +223,6 @@ static int acpi_power_get_list_state(struct list_head *list, int *state) return 0; } -static void acpi_power_resume_dependent(struct work_struct *work) -{ - struct acpi_power_dependent_device *dep; - struct acpi_device_physical_node *pn; - struct acpi_device *adev; - int state; - - dep = container_of(work, struct acpi_power_dependent_device, work); - adev = dep->adev; - if (acpi_power_get_inferred_state(adev, &state)) - return; - - if (state > ACPI_STATE_D0) - return; - - mutex_lock(&adev->physical_node_lock); - - list_for_each_entry(pn, &adev->physical_node_list, node) - pm_request_resume(pn->dev); - - list_for_each_entry(pn, &adev->power_dependent, node) - pm_request_resume(pn->dev); - - mutex_unlock(&adev->physical_node_lock); -} - static int __acpi_power_on(struct acpi_power_resource *resource) { acpi_status status = AE_OK; @@ -272,30 +237,29 @@ 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, - "Power resource [%s] already on", + "Power resource [%s] already on\n", resource->name)); } else { result = __acpi_power_on(resource); - if (result) { + if (result) resource->ref_count--; - } else { - struct acpi_power_dependent_device *dep; - - list_for_each_entry(dep, &resource->dependent, node) - 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 +277,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", + "Power resource [%s] already off\n", resource->name)); - goto unlock; + return 0; } if (--resource->ref_count) { @@ -335,10 +297,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; } @@ -380,52 +348,6 @@ static int acpi_power_on_list(struct list_head *list) return result; } -static void acpi_power_add_dependent(struct acpi_power_resource *resource, - struct acpi_device *adev) -{ - struct acpi_power_dependent_device *dep; - - mutex_lock(&resource->resource_lock); - - list_for_each_entry(dep, &resource->dependent, node) - if (dep->adev == adev) - goto out; - - dep = kzalloc(sizeof(*dep), GFP_KERNEL); - if (!dep) - goto out; - - dep->adev = adev; - INIT_WORK(&dep->work, acpi_power_resume_dependent); - list_add_tail(&dep->node, &resource->dependent); - - out: - mutex_unlock(&resource->resource_lock); -} - -static void acpi_power_remove_dependent(struct acpi_power_resource *resource, - struct acpi_device *adev) -{ - struct acpi_power_dependent_device *dep; - struct work_struct *work = NULL; - - mutex_lock(&resource->resource_lock); - - list_for_each_entry(dep, &resource->dependent, node) - if (dep->adev == adev) { - list_del(&dep->node); - work = &dep->work; - break; - } - - mutex_unlock(&resource->resource_lock); - - if (work) { - cancel_work_sync(work); - kfree(dep); - } -} - static struct attribute *attrs[] = { NULL, }; @@ -449,90 +371,115 @@ static struct attribute_group attr_groups[] = { }, }; -static void acpi_power_hide_list(struct acpi_device *adev, int state) +static struct attribute_group wakeup_attr_group = { + .name = "power_resources_wakeup", + .attrs = attrs, +}; + +static void acpi_power_hide_list(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group) { - struct acpi_device_power_state *ps = &adev->power.states[state]; struct acpi_power_resource_entry *entry; - if (list_empty(&ps->resources)) + if (list_empty(resources)) return; - list_for_each_entry_reverse(entry, &ps->resources, node) { + list_for_each_entry_reverse(entry, resources, node) { struct acpi_device *res_dev = &entry->resource->device; sysfs_remove_link_from_group(&adev->dev.kobj, - attr_groups[state].name, + attr_group->name, dev_name(&res_dev->dev)); } - sysfs_remove_group(&adev->dev.kobj, &attr_groups[state]); + sysfs_remove_group(&adev->dev.kobj, attr_group); } -static void acpi_power_expose_list(struct acpi_device *adev, int state) +static void acpi_power_expose_list(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group) { - struct acpi_device_power_state *ps = &adev->power.states[state]; struct acpi_power_resource_entry *entry; int ret; - if (list_empty(&ps->resources)) + if (list_empty(resources)) return; - ret = sysfs_create_group(&adev->dev.kobj, &attr_groups[state]); + ret = sysfs_create_group(&adev->dev.kobj, attr_group); if (ret) return; - list_for_each_entry(entry, &ps->resources, node) { + list_for_each_entry(entry, resources, node) { struct acpi_device *res_dev = &entry->resource->device; ret = sysfs_add_link_to_group(&adev->dev.kobj, - attr_groups[state].name, + attr_group->name, &res_dev->dev.kobj, dev_name(&res_dev->dev)); if (ret) { - acpi_power_hide_list(adev, state); + acpi_power_hide_list(adev, resources, attr_group); break; } } } +static void acpi_power_expose_hide(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group, + bool expose) +{ + if (expose) + acpi_power_expose_list(adev, resources, attr_group); + else + acpi_power_hide_list(adev, resources, attr_group); +} + void acpi_power_add_remove_device(struct acpi_device *adev, bool add) { - struct acpi_device_power_state *ps; - struct acpi_power_resource_entry *entry; int state; + if (adev->wakeup.flags.valid) + acpi_power_expose_hide(adev, &adev->wakeup.resources, + &wakeup_attr_group, add); + if (!adev->power.flags.power_resources) return; - ps = &adev->power.states[ACPI_STATE_D0]; - list_for_each_entry(entry, &ps->resources, node) { - struct acpi_power_resource *resource = entry->resource; - - if (add) - acpi_power_add_dependent(resource, adev); - else - acpi_power_remove_dependent(resource, adev); - } - - for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) { - if (add) - acpi_power_expose_list(adev, state); - else - acpi_power_hide_list(adev, state); - } + for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) + acpi_power_expose_hide(adev, + &adev->power.states[state].resources, + &attr_groups[state], 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; } /* -------------------------------------------------------------------------- @@ -590,9 +537,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev, } /* Execute _PSW */ - arg_list.count = 1; - in_arg[0].integer.value = enable; - status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL); + status = acpi_execute_simple_method(dev->handle, "_PSW", enable); if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { printk(KERN_ERR PREFIX "_PSW execution failed\n"); dev->wakeup.flags.valid = 0; @@ -610,6 +555,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 +566,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 +607,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 +629,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: @@ -708,7 +684,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) } } - *state = ACPI_STATE_D3; + *state = ACPI_STATE_D3_COLD; return 0; } @@ -766,7 +742,7 @@ static void acpi_release_power_resource(struct device *dev) list_del(&resource->list_node); mutex_unlock(&power_resource_list_lock); - acpi_free_ids(device); + acpi_free_pnp_ids(&device->pnp); kfree(resource); } @@ -806,7 +782,7 @@ int acpi_add_power_resource(acpi_handle handle) acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, ACPI_STA_DEFAULT); mutex_init(&resource->resource_lock); - INIT_LIST_HEAD(&resource->dependent); + INIT_LIST_HEAD(&resource->list_node); resource->name = device->pnp.bus_id; strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); @@ -859,8 +835,10 @@ void acpi_resume_power_resources(void) mutex_lock(&resource->resource_lock); result = acpi_power_get_state(resource->device.handle, &state); - if (result) + if (result) { + mutex_unlock(&resource->resource_lock); continue; + } if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count) { |
