summaryrefslogtreecommitdiff
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r--drivers/acpi/power.c219
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);