diff options
Diffstat (limited to 'drivers/acpi/power.c')
| -rw-r--r-- | drivers/acpi/power.c | 726 |
1 files changed, 480 insertions, 246 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 67dedeed144..e0bcfb642b5 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -40,11 +40,11 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/slab.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/pm_runtime.h> +#include <linux/sysfs.h> +#include <linux/acpi.h> #include "sleep.h" - -#define PREFIX "ACPI: " +#include "internal.h" #define _COMPONENT ACPI_POWER_COMPONENT ACPI_MODULE_NAME("power"); @@ -56,69 +56,115 @@ ACPI_MODULE_NAME("power"); #define ACPI_POWER_RESOURCE_STATE_ON 0x01 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF -int acpi_power_nocheck; -module_param_named(power_nocheck, acpi_power_nocheck, bool, 000); - -static int acpi_power_add(struct acpi_device *device); -static int acpi_power_remove(struct acpi_device *device, int type); -static int acpi_power_resume(struct acpi_device *device); - -static const struct acpi_device_id power_device_ids[] = { - {ACPI_POWER_HID, 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, power_device_ids); - -static struct acpi_driver acpi_power_driver = { - .name = "power", - .class = ACPI_POWER_CLASS, - .ids = power_device_ids, - .ops = { - .add = acpi_power_add, - .remove = acpi_power_remove, - .resume = acpi_power_resume, - }, -}; - struct acpi_power_resource { - struct acpi_device * device; - acpi_bus_id name; + struct acpi_device device; + struct list_head list_node; + char *name; u32 system_level; u32 order; unsigned int ref_count; + bool wakeup_enabled; struct mutex resource_lock; }; -static struct list_head acpi_power_resource_list; +struct acpi_power_resource_entry { + struct list_head node; + struct acpi_power_resource *resource; +}; + +static LIST_HEAD(acpi_power_resource_list); +static DEFINE_MUTEX(power_resource_list_lock); /* -------------------------------------------------------------------------- Power Resource Management -------------------------------------------------------------------------- */ -static int -acpi_power_get_context(acpi_handle handle, - struct acpi_power_resource **resource) +static inline +struct acpi_power_resource *to_power_resource(struct acpi_device *device) { - int result = 0; - struct acpi_device *device = NULL; + return container_of(device, struct acpi_power_resource, device); +} +static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) +{ + struct acpi_device *device; - if (!resource) - return -ENODEV; + if (acpi_bus_get_device(handle, &device)) + return NULL; - result = acpi_bus_get_device(handle, &device); - if (result) { - printk(KERN_WARNING PREFIX "Getting context [%p]\n", handle); - return result; - } + return to_power_resource(device); +} - *resource = acpi_driver_data(device); - if (!*resource) - return -ENODEV; +static int acpi_power_resources_list_add(acpi_handle handle, + struct list_head *list) +{ + struct acpi_power_resource *resource = acpi_power_get_context(handle); + struct acpi_power_resource_entry *entry; + + if (!resource || !list) + return -EINVAL; + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->resource = resource; + if (!list_empty(list)) { + struct acpi_power_resource_entry *e; + + list_for_each_entry(e, list, node) + if (e->resource->order > resource->order) { + list_add_tail(&entry->node, &e->node); + return 0; + } + } + list_add_tail(&entry->node, list); return 0; } +void acpi_power_resources_list_free(struct list_head *list) +{ + struct acpi_power_resource_entry *entry, *e; + + list_for_each_entry_safe(entry, e, list, node) { + list_del(&entry->node); + kfree(entry); + } +} + +int acpi_extract_power_resources(union acpi_object *package, unsigned int start, + struct list_head *list) +{ + unsigned int i; + int err = 0; + + for (i = start; i < package->package.count; i++) { + union acpi_object *element = &package->package.elements[i]; + acpi_handle rhandle; + + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { + err = -ENODATA; + break; + } + rhandle = element->reference.handle; + if (!rhandle) { + err = -ENODEV; + break; + } + err = acpi_add_power_resource(rhandle); + if (err) + break; + + err = acpi_power_resources_list_add(rhandle, list); + if (err) + break; + } + if (err) + acpi_power_resources_list_free(list); + + return err; +} + static int acpi_power_get_state(acpi_handle handle, int *state) { acpi_status status = AE_OK; @@ -146,124 +192,300 @@ static int acpi_power_get_state(acpi_handle handle, int *state) return 0; } -static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) +static int acpi_power_get_list_state(struct list_head *list, int *state) { - int result = 0, state1; - u32 i = 0; - + struct acpi_power_resource_entry *entry; + int cur_state; if (!list || !state) return -EINVAL; /* The state of the list is 'on' IFF all resources are 'on'. */ - - for (i = 0; i < list->count; i++) { - /* - * The state of the power resource can be obtained by - * using the ACPI handle. In such case it is unnecessary to - * get the Power resource first and then get its state again. - */ - result = acpi_power_get_state(list->handles[i], &state1); + list_for_each_entry(entry, list, node) { + struct acpi_power_resource *resource = entry->resource; + acpi_handle handle = resource->device.handle; + int result; + + mutex_lock(&resource->resource_lock); + result = acpi_power_get_state(handle, &cur_state); + mutex_unlock(&resource->resource_lock); if (result) return result; - *state = state1; - - if (*state != ACPI_POWER_RESOURCE_STATE_ON) + if (cur_state != ACPI_POWER_RESOURCE_STATE_ON) break; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n", - *state ? "on" : "off")); + cur_state ? "on" : "off")); - return result; + *state = cur_state; + return 0; } static int __acpi_power_on(struct acpi_power_resource *resource) { acpi_status status = AE_OK; - status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); + status = acpi_evaluate_object(resource->device.handle, "_ON", NULL, NULL); if (ACPI_FAILURE(status)) return -ENODEV; - /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", resource->name)); return 0; } -static int acpi_power_on(acpi_handle handle) +static int acpi_power_on_unlocked(struct acpi_power_resource *resource) { int result = 0; - struct acpi_power_resource *resource = NULL; - - result = acpi_power_get_context(handle, &resource); - if (result) - return result; - - mutex_lock(&resource->resource_lock); 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) + resource->ref_count--; } + return result; +} + +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; +} +static int __acpi_power_off(struct acpi_power_resource *resource) +{ + acpi_status status; + + status = acpi_evaluate_object(resource->device.handle, "_OFF", + NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned off\n", + resource->name)); return 0; } -static int acpi_power_off_device(acpi_handle handle) +static int acpi_power_off_unlocked(struct acpi_power_resource *resource) { int result = 0; - acpi_status status = AE_OK; - struct acpi_power_resource *resource = NULL; - - result = acpi_power_get_context(handle, &resource); - if (result) - return result; - - 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) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] still in use\n", resource->name)); - goto unlock; + } else { + result = __acpi_power_off(resource); + if (result) + resource->ref_count++; } + return result; +} - status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - } else { - /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D3; +static int acpi_power_off(struct acpi_power_resource *resource) +{ + int result; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Power resource [%s] turned off\n", - resource->name)); + mutex_lock(&resource->resource_lock); + result = acpi_power_off_unlocked(resource); + mutex_unlock(&resource->resource_lock); + return result; +} + +static int acpi_power_off_list(struct list_head *list) +{ + struct acpi_power_resource_entry *entry; + int result = 0; + + list_for_each_entry_reverse(entry, list, node) { + result = acpi_power_off(entry->resource); + if (result) + goto err; } + return 0; - unlock: - mutex_unlock(&resource->resource_lock); + err: + list_for_each_entry_continue(entry, list, node) + acpi_power_on(entry->resource); return result; } +static int acpi_power_on_list(struct list_head *list) +{ + struct acpi_power_resource_entry *entry; + int result = 0; + + list_for_each_entry(entry, list, node) { + result = acpi_power_on(entry->resource); + if (result) + goto err; + } + return 0; + + err: + list_for_each_entry_continue_reverse(entry, list, node) + acpi_power_off(entry->resource); + + return result; +} + +static struct attribute *attrs[] = { + NULL, +}; + +static struct attribute_group attr_groups[] = { + [ACPI_STATE_D0] = { + .name = "power_resources_D0", + .attrs = attrs, + }, + [ACPI_STATE_D1] = { + .name = "power_resources_D1", + .attrs = attrs, + }, + [ACPI_STATE_D2] = { + .name = "power_resources_D2", + .attrs = attrs, + }, + [ACPI_STATE_D3_HOT] = { + .name = "power_resources_D3hot", + .attrs = attrs, + }, +}; + +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_power_resource_entry *entry; + + if (list_empty(resources)) + return; + + 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_group->name, + dev_name(&res_dev->dev)); + } + sysfs_remove_group(&adev->dev.kobj, attr_group); +} + +static void acpi_power_expose_list(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group) +{ + struct acpi_power_resource_entry *entry; + int ret; + + if (list_empty(resources)) + return; + + ret = sysfs_create_group(&adev->dev.kobj, attr_group); + if (ret) + return; + + 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_group->name, + &res_dev->dev.kobj, + dev_name(&res_dev->dev)); + if (ret) { + 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) +{ + 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; + + 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_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); + } + *system_level_p = system_level; + return 0; +} + +/* -------------------------------------------------------------------------- + Device Power Management + -------------------------------------------------------------------------- */ + /** * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in * ACPI 3.0) _PSW (Power State Wake) @@ -315,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; @@ -335,7 +555,8 @@ int acpi_device_sleep_wake(struct acpi_device *dev, */ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) { - int i, err = 0; + struct acpi_power_resource_entry *entry; + int err = 0; if (!dev || !dev->wakeup.flags.valid) return -EINVAL; @@ -345,24 +566,31 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) if (dev->wakeup.prepare_count++) goto out; - /* Open power resource */ - for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_on(dev->wakeup.resources.handles[i]); - if (ret) { - printk(KERN_ERR PREFIX "Transition power state\n"); + 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; - err = -ENODEV; - goto err_out; + goto out; } } - /* - * Passing 3 as the third argument below means the device may be placed - * in arbitrary power state afterwards. + * 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); - - err_out: if (err) dev->wakeup.prepare_count = 0; @@ -379,7 +607,8 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) */ int acpi_disable_wakeup_device_power(struct acpi_device *dev) { - int i, err = 0; + struct acpi_power_resource_entry *entry; + int err = 0; if (!dev || !dev->wakeup.flags.valid) return -EINVAL; @@ -400,15 +629,24 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) if (err) goto out; - /* Close power resource */ - for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_off_device( - dev->wakeup.resources.handles[i]); - if (ret) { - printk(KERN_ERR PREFIX "Transition power state\n"); + 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; - err = -ENODEV; - goto out; + break; } } @@ -417,30 +655,23 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) return err; } -/* -------------------------------------------------------------------------- - Device Power Management - -------------------------------------------------------------------------- */ - -int acpi_power_get_inferred_state(struct acpi_device *device) +int acpi_power_get_inferred_state(struct acpi_device *device, int *state) { int result = 0; - struct acpi_handle_list *list = NULL; int list_state = 0; int i = 0; - - if (!device) + if (!device || !state) return -EINVAL; - device->power.state = ACPI_STATE_UNKNOWN; - /* * We know a device's inferred power state when all the resources * required for a given D-state are 'on'. */ - for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) { - list = &device->power.states[i].resources; - if (list->count < 1) + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { + struct list_head *list = &device->power.states[i].resources; + + if (list_empty(list)) continue; result = acpi_power_get_list_state(list, &list_state); @@ -448,177 +679,180 @@ int acpi_power_get_inferred_state(struct acpi_device *device) return result; if (list_state == ACPI_POWER_RESOURCE_STATE_ON) { - device->power.state = i; + *state = i; return 0; } } - device->power.state = ACPI_STATE_D3; - + *state = ACPI_STATE_D3_COLD; return 0; } +int acpi_power_on_resources(struct acpi_device *device, int state) +{ + if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_HOT) + return -EINVAL; + + return acpi_power_on_list(&device->power.states[state].resources); +} + int acpi_power_transition(struct acpi_device *device, int state) { int result = 0; - struct acpi_handle_list *cl = NULL; /* Current Resources */ - struct acpi_handle_list *tl = NULL; /* Target Resources */ - int i = 0; - - if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) + if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) return -EINVAL; + if (device->power.state == state || !device->flags.power_manageable) + return 0; + if ((device->power.state < ACPI_STATE_D0) - || (device->power.state > ACPI_STATE_D3)) + || (device->power.state > ACPI_STATE_D3_COLD)) return -ENODEV; - cl = &device->power.states[device->power.state].resources; - tl = &device->power.states[state].resources; - /* TBD: Resources must be ordered. */ /* * First we reference all power resources required in the target list - * (e.g. so the device doesn't lose power while transitioning). + * (e.g. so the device doesn't lose power while transitioning). Then, + * we dereference all power resources used in the current list. */ - for (i = 0; i < tl->count; i++) { - result = acpi_power_on(tl->handles[i]); - if (result) - goto end; - } + if (state < ACPI_STATE_D3_COLD) + result = acpi_power_on_list( + &device->power.states[state].resources); - if (device->power.state == state) { - goto end; - } + if (!result && device->power.state < ACPI_STATE_D3_COLD) + acpi_power_off_list( + &device->power.states[device->power.state].resources); - /* - * Then we dereference all power resources used in the current list. - */ - for (i = 0; i < cl->count; i++) { - result = acpi_power_off_device(cl->handles[i]); - if (result) - goto end; - } - - end: - if (result) - device->power.state = ACPI_STATE_UNKNOWN; - else { - /* We shouldn't change the state till all above operations succeed */ - device->power.state = state; - } + /* We shouldn't change the state unless the above operations succeed. */ + device->power.state = result ? ACPI_STATE_UNKNOWN : state; return result; } -/* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ +static void acpi_release_power_resource(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct acpi_power_resource *resource; -static int acpi_power_add(struct acpi_device *device) + resource = container_of(device, struct acpi_power_resource, device); + + mutex_lock(&power_resource_list_lock); + list_del(&resource->list_node); + mutex_unlock(&power_resource_list_lock); + + acpi_free_pnp_ids(&device->pnp); + kfree(resource); +} + +static ssize_t acpi_power_in_use_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct acpi_power_resource *resource; + + resource = to_power_resource(to_acpi_device(dev)); + return sprintf(buf, "%u\n", !!resource->ref_count); +} +static DEVICE_ATTR(resource_in_use, 0444, acpi_power_in_use_show, NULL); + +static void acpi_power_sysfs_remove(struct acpi_device *device) { - int result = 0, state; - acpi_status status = AE_OK; - struct acpi_power_resource *resource = NULL; + device_remove_file(&device->dev, &dev_attr_resource_in_use); +} + +int acpi_add_power_resource(acpi_handle handle) +{ + struct acpi_power_resource *resource; + struct acpi_device *device = NULL; union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; + acpi_status status; + int state, result = -ENODEV; + acpi_bus_get_device(handle, &device); + if (device) + return 0; - if (!device) - return -EINVAL; - - resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL); + resource = kzalloc(sizeof(*resource), GFP_KERNEL); if (!resource) return -ENOMEM; - resource->device = device; + device = &resource->device; + acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, + ACPI_STA_DEFAULT); mutex_init(&resource->resource_lock); - strcpy(resource->name, device->pnp.bus_id); + 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); - device->driver_data = resource; + device->power.state = ACPI_STATE_UNKNOWN; /* Evalute the object to get the system level and resource order. */ - status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } + status = acpi_evaluate_object(handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) + goto err; + resource->system_level = acpi_object.power_resource.system_level; resource->order = acpi_object.power_resource.resource_order; - result = acpi_power_get_state(device->handle, &state); + result = acpi_power_get_state(handle, &state); if (result) - goto end; - - switch (state) { - case ACPI_POWER_RESOURCE_STATE_ON: - device->power.state = ACPI_STATE_D0; - break; - case ACPI_POWER_RESOURCE_STATE_OFF: - device->power.state = ACPI_STATE_D3; - break; - default: - device->power.state = ACPI_STATE_UNKNOWN; - break; - } + goto err; printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), state ? "on" : "off"); - end: + device->flags.match_driver = true; + result = acpi_device_add(device, acpi_release_power_resource); if (result) - kfree(resource); - - return result; -} - -static int acpi_power_remove(struct acpi_device *device, int type) -{ - struct acpi_power_resource *resource; - - if (!device) - return -EINVAL; - - resource = acpi_driver_data(device); - if (!resource) - return -EINVAL; + goto err; - kfree(resource); + if (!device_create_file(&device->dev, &dev_attr_resource_in_use)) + device->remove = acpi_power_sysfs_remove; + mutex_lock(&power_resource_list_lock); + list_add(&resource->list_node, &acpi_power_resource_list); + mutex_unlock(&power_resource_list_lock); + acpi_device_add_finalize(device); return 0; + + err: + acpi_release_power_resource(&device->dev); + return result; } -static int acpi_power_resume(struct acpi_device *device) +#ifdef CONFIG_ACPI_SLEEP +void acpi_resume_power_resources(void) { - int result = 0, state; struct acpi_power_resource *resource; - if (!device) - return -EINVAL; - - resource = acpi_driver_data(device); - if (!resource) - return -EINVAL; + mutex_lock(&power_resource_list_lock); - mutex_lock(&resource->resource_lock); + list_for_each_entry(resource, &acpi_power_resource_list, list_node) { + int result, state; - result = acpi_power_get_state(device->handle, &state); - if (result) - goto unlock; + mutex_lock(&resource->resource_lock); - if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count) - result = __acpi_power_on(resource); + result = acpi_power_get_state(resource->device.handle, &state); + if (result) { + mutex_unlock(&resource->resource_lock); + continue; + } - unlock: - mutex_unlock(&resource->resource_lock); + if (state == ACPI_POWER_RESOURCE_STATE_OFF + && resource->ref_count) { + dev_info(&resource->device.dev, "Turning ON\n"); + __acpi_power_on(resource); + } else if (state == ACPI_POWER_RESOURCE_STATE_ON + && !resource->ref_count) { + dev_info(&resource->device.dev, "Turning OFF\n"); + __acpi_power_off(resource); + } - return result; -} + mutex_unlock(&resource->resource_lock); + } -int __init acpi_power_init(void) -{ - INIT_LIST_HEAD(&acpi_power_resource_list); - return acpi_bus_register_driver(&acpi_power_driver); + mutex_unlock(&power_resource_list_lock); } +#endif |
