diff options
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r-- | drivers/acpi/power.c | 219 |
1 files changed, 82 insertions, 137 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 6e7b9d523812..659386c4f0cb 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -83,31 +83,20 @@ static struct acpi_driver acpi_power_driver = { .drv.pm = &acpi_power_pm, }; -/* - * A power managed device - * A device may rely on multiple power resources. - * */ -struct acpi_power_managed_device { - struct device *dev; /* The physical device */ - acpi_handle *handle; -}; - -struct acpi_power_resource_device { - struct acpi_power_managed_device *device; - struct acpi_power_resource_device *next; +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 acpi_device *device; + struct list_head dependent; acpi_bus_id name; u32 system_level; u32 order; unsigned int ref_count; struct mutex resource_lock; - - /* List of devices relying on this power resource */ - struct acpi_power_resource_device *devices; - struct mutex devices_lock; }; static struct list_head acpi_power_resource_list; @@ -207,21 +196,30 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) return 0; } -/* Resume the device when all power resources in _PR0 are on */ -static void acpi_power_on_device(struct acpi_power_managed_device *device) +static void acpi_power_resume_dependent(struct work_struct *work) { - struct acpi_device *acpi_dev; - acpi_handle handle = device->handle; + struct acpi_power_dependent_device *dep; + struct acpi_device_physical_node *pn; + struct acpi_device *adev; int state; - if (acpi_bus_get_device(handle, &acpi_dev)) + dep = container_of(work, struct acpi_power_dependent_device, work); + adev = dep->adev; + if (acpi_power_get_inferred_state(adev, &state)) return; - if(acpi_power_get_inferred_state(acpi_dev, &state)) + if (state > ACPI_STATE_D0) return; - if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev)) - pm_request_resume(device->dev); + 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) @@ -244,9 +242,7 @@ static int __acpi_power_on(struct acpi_power_resource *resource) static int acpi_power_on(acpi_handle handle) { int result = 0; - bool resume_device = false; struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *device_list; result = acpi_power_get_context(handle, &resource); if (result) @@ -260,26 +256,17 @@ static int acpi_power_on(acpi_handle handle) resource->name)); } else { result = __acpi_power_on(resource); - if (result) + if (result) { resource->ref_count--; - else - resume_device = true; - } + } else { + struct acpi_power_dependent_device *dep; - mutex_unlock(&resource->resource_lock); - - if (!resume_device) - return result; - - mutex_lock(&resource->devices_lock); - - device_list = resource->devices; - while (device_list) { - acpi_power_on_device(device_list->device); - device_list = device_list->next; + list_for_each_entry(dep, &resource->dependent, node) + schedule_work(&dep->work); + } } - mutex_unlock(&resource->devices_lock); + mutex_unlock(&resource->resource_lock); return result; } @@ -357,119 +344,81 @@ static int acpi_power_on_list(struct acpi_handle_list *list) return result; } -static void __acpi_power_resource_unregister_device(struct device *dev, - acpi_handle res_handle) +static void acpi_power_add_dependent(acpi_handle rhandle, + struct acpi_device *adev) { - struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *prev, *curr; + struct acpi_power_dependent_device *dep; + struct acpi_power_resource *resource; - if (acpi_power_get_context(res_handle, &resource)) + if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) return; - mutex_lock(&resource->devices_lock); - prev = NULL; - curr = resource->devices; - while (curr) { - if (curr->device->dev == dev) { - if (!prev) - resource->devices = curr->next; - else - prev->next = curr->next; - - kfree(curr); - break; - } - - prev = curr; - curr = curr->next; - } - mutex_unlock(&resource->devices_lock); -} - -/* Unlink dev from all power resources in _PR0 */ -void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle) -{ - struct acpi_device *acpi_dev; - struct acpi_handle_list *list; - int i; + mutex_lock(&resource->resource_lock); - if (!dev || !handle) - return; + list_for_each_entry(dep, &resource->dependent, node) + if (dep->adev == adev) + goto out; - if (acpi_bus_get_device(handle, &acpi_dev)) - return; + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (!dep) + goto out; - list = &acpi_dev->power.states[ACPI_STATE_D0].resources; + dep->adev = adev; + INIT_WORK(&dep->work, acpi_power_resume_dependent); + list_add_tail(&dep->node, &resource->dependent); - for (i = 0; i < list->count; i++) - __acpi_power_resource_unregister_device(dev, - list->handles[i]); + out: + mutex_unlock(&resource->resource_lock); } -EXPORT_SYMBOL_GPL(acpi_power_resource_unregister_device); -static int __acpi_power_resource_register_device( - struct acpi_power_managed_device *powered_device, acpi_handle handle) +static void acpi_power_remove_dependent(acpi_handle rhandle, + struct acpi_device *adev) { - struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *power_resource_device; - int result; + struct acpi_power_dependent_device *dep; + struct acpi_power_resource *resource; + struct work_struct *work = NULL; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) + return; - power_resource_device = kzalloc( - sizeof(*power_resource_device), GFP_KERNEL); - if (!power_resource_device) - return -ENOMEM; + mutex_lock(&resource->resource_lock); - power_resource_device->device = powered_device; + list_for_each_entry(dep, &resource->dependent, node) + if (dep->adev == adev) { + list_del(&dep->node); + work = &dep->work; + break; + } - mutex_lock(&resource->devices_lock); - power_resource_device->next = resource->devices; - resource->devices = power_resource_device; - mutex_unlock(&resource->devices_lock); + mutex_unlock(&resource->resource_lock); - return 0; + if (work) { + cancel_work_sync(work); + kfree(dep); + } } -/* Link dev to all power resources in _PR0 */ -int acpi_power_resource_register_device(struct device *dev, acpi_handle handle) +void acpi_power_add_remove_device(struct acpi_device *adev, bool add) { - struct acpi_device *acpi_dev; - struct acpi_handle_list *list; - struct acpi_power_managed_device *powered_device; - int i, ret; + if (adev->power.flags.power_resources) { + struct acpi_device_power_state *ps; + int j; - if (!dev || !handle) - return -ENODEV; - - ret = acpi_bus_get_device(handle, &acpi_dev); - if (ret || !acpi_dev->power.flags.power_resources) - return -ENODEV; + ps = &adev->power.states[ACPI_STATE_D0]; + for (j = 0; j < ps->resources.count; j++) { + acpi_handle rhandle = ps->resources.handles[j]; - powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL); - if (!powered_device) - return -ENOMEM; - - powered_device->dev = dev; - powered_device->handle = handle; - - list = &acpi_dev->power.states[ACPI_STATE_D0].resources; - - for (i = 0; i < list->count; i++) { - ret = __acpi_power_resource_register_device(powered_device, - list->handles[i]); - - if (ret) { - acpi_power_resource_unregister_device(dev, handle); - break; + if (add) + acpi_power_add_dependent(rhandle, adev); + else + acpi_power_remove_dependent(rhandle, adev); } } - - return ret; } -EXPORT_SYMBOL_GPL(acpi_power_resource_register_device); + +/* -------------------------------------------------------------------------- + Device Power Management + -------------------------------------------------------------------------- */ /** * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in @@ -623,10 +572,6 @@ 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 *state) { int result = 0; @@ -725,7 +670,7 @@ static int acpi_power_add(struct acpi_device *device) resource->device = device; mutex_init(&resource->resource_lock); - mutex_init(&resource->devices_lock); + INIT_LIST_HEAD(&resource->dependent); strcpy(resource->name, device->pnp.bus_id); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); |