Commit 1004879f authored by Len Brown's avatar Len Brown

Pull bugzilla-7122 into release branch

parents f0e5ed7f e8363f33
...@@ -56,6 +56,7 @@ ACPI_MODULE_NAME("power"); ...@@ -56,6 +56,7 @@ ACPI_MODULE_NAME("power");
#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
static int acpi_power_add(struct acpi_device *device); static int acpi_power_add(struct acpi_device *device);
static int acpi_power_remove(struct acpi_device *device, int type); static int acpi_power_remove(struct acpi_device *device, int type);
static int acpi_power_resume(struct acpi_device *device);
static int acpi_power_open_fs(struct inode *inode, struct file *file); static int acpi_power_open_fs(struct inode *inode, struct file *file);
static struct acpi_driver acpi_power_driver = { static struct acpi_driver acpi_power_driver = {
...@@ -65,16 +66,23 @@ static struct acpi_driver acpi_power_driver = { ...@@ -65,16 +66,23 @@ static struct acpi_driver acpi_power_driver = {
.ops = { .ops = {
.add = acpi_power_add, .add = acpi_power_add,
.remove = acpi_power_remove, .remove = acpi_power_remove,
.resume = acpi_power_resume,
}, },
}; };
struct acpi_power_reference {
struct list_head node;
struct acpi_device *device;
};
struct acpi_power_resource { struct acpi_power_resource {
struct acpi_device * device; struct acpi_device * device;
acpi_bus_id name; acpi_bus_id name;
u32 system_level; u32 system_level;
u32 order; u32 order;
int state; int state;
int references; struct mutex resource_lock;
struct list_head reference;
}; };
static struct list_head acpi_power_resource_list; static struct list_head acpi_power_resource_list;
...@@ -170,22 +178,47 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) ...@@ -170,22 +178,47 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
return result; return result;
} }
static int acpi_power_on(acpi_handle handle) static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
{ {
int result = 0; int result = 0;
int found = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
struct acpi_device *device = NULL;
struct acpi_power_resource *resource = NULL; struct acpi_power_resource *resource = NULL;
struct list_head *node, *next;
struct acpi_power_reference *ref;
result = acpi_power_get_context(handle, &resource); result = acpi_power_get_context(handle, &resource);
if (result) if (result)
return result; return result;
resource->references++; mutex_lock(&resource->resource_lock);
list_for_each_safe(node, next, &resource->reference) {
ref = container_of(node, struct acpi_power_reference, node);
if (dev->handle == ref->device->handle) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n",
dev->pnp.bus_id, resource->name));
found = 1;
break;
}
}
if (!found) {
ref = kmalloc(sizeof (struct acpi_power_reference),
irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
if (!ref) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n"));
mutex_unlock(&resource->resource_lock);
return -ENOMEM;
}
list_add_tail(&ref->node, &resource->reference);
ref->device = dev;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n",
dev->pnp.bus_id, resource->name));
}
mutex_unlock(&resource->resource_lock);
if ((resource->references > 1) if (resource->state == ACPI_POWER_RESOURCE_STATE_ON) {
|| (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n", ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
resource->name)); resource->name));
return 0; return 0;
...@@ -202,38 +235,49 @@ static int acpi_power_on(acpi_handle handle) ...@@ -202,38 +235,49 @@ static int acpi_power_on(acpi_handle handle)
return -ENOEXEC; return -ENOEXEC;
/* Update the power resource's _device_ power state */ /* Update the power resource's _device_ power state */
device = resource->device;
resource->device->power.state = ACPI_STATE_D0; resource->device->power.state = ACPI_STATE_D0;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
resource->name)); resource->name));
return 0; return 0;
} }
static int acpi_power_off_device(acpi_handle handle) static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
{ {
int result = 0; int result = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
struct acpi_power_resource *resource = NULL; struct acpi_power_resource *resource = NULL;
struct list_head *node, *next;
struct acpi_power_reference *ref;
result = acpi_power_get_context(handle, &resource); result = acpi_power_get_context(handle, &resource);
if (result) if (result)
return result; return result;
if (resource->references) mutex_lock(&resource->resource_lock);
resource->references--; list_for_each_safe(node, next, &resource->reference) {
ref = container_of(node, struct acpi_power_reference, node);
if (dev->handle == ref->device->handle) {
list_del(&ref->node);
kfree(ref);
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n",
dev->pnp.bus_id, resource->name));
break;
}
}
if (resource->references) { if (!list_empty(&resource->reference)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n",
"Resource [%s] is still in use, dereferencing\n", resource->name));
resource->device->pnp.bus_id)); mutex_unlock(&resource->resource_lock);
return 0; return 0;
} }
mutex_unlock(&resource->resource_lock);
if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
resource->device->pnp.bus_id)); resource->name));
return 0; return 0;
} }
...@@ -275,7 +319,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev) ...@@ -275,7 +319,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev)
arg.integer.value = 1; arg.integer.value = 1;
/* Open power resource */ /* Open power resource */
for (i = 0; i < dev->wakeup.resources.count; i++) { for (i = 0; i < dev->wakeup.resources.count; i++) {
ret = acpi_power_on(dev->wakeup.resources.handles[i]); ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
if (ret) { if (ret) {
printk(KERN_ERR PREFIX "Transition power state\n"); printk(KERN_ERR PREFIX "Transition power state\n");
dev->wakeup.flags.valid = 0; dev->wakeup.flags.valid = 0;
...@@ -322,7 +366,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) ...@@ -322,7 +366,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
/* Close power resource */ /* Close power resource */
for (i = 0; i < dev->wakeup.resources.count; i++) { for (i = 0; i < dev->wakeup.resources.count; i++) {
ret = acpi_power_off_device(dev->wakeup.resources.handles[i]); ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev);
if (ret) { if (ret) {
printk(KERN_ERR PREFIX "Transition power state\n"); printk(KERN_ERR PREFIX "Transition power state\n");
dev->wakeup.flags.valid = 0; dev->wakeup.flags.valid = 0;
...@@ -406,7 +450,7 @@ int acpi_power_transition(struct acpi_device *device, int state) ...@@ -406,7 +450,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
* (e.g. so the device doesn't lose power while transitioning). * (e.g. so the device doesn't lose power while transitioning).
*/ */
for (i = 0; i < tl->count; i++) { for (i = 0; i < tl->count; i++) {
result = acpi_power_on(tl->handles[i]); result = acpi_power_on(tl->handles[i], device);
if (result) if (result)
goto end; goto end;
} }
...@@ -415,7 +459,7 @@ int acpi_power_transition(struct acpi_device *device, int state) ...@@ -415,7 +459,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
* Then we dereference all power resources used in the current list. * Then we dereference all power resources used in the current list.
*/ */
for (i = 0; i < cl->count; i++) { for (i = 0; i < cl->count; i++) {
result = acpi_power_off_device(cl->handles[i]); result = acpi_power_off_device(cl->handles[i], device);
if (result) if (result)
goto end; goto end;
} }
...@@ -438,7 +482,11 @@ static struct proc_dir_entry *acpi_power_dir; ...@@ -438,7 +482,11 @@ static struct proc_dir_entry *acpi_power_dir;
static int acpi_power_seq_show(struct seq_file *seq, void *offset) static int acpi_power_seq_show(struct seq_file *seq, void *offset)
{ {
int count = 0;
int result = 0;
struct acpi_power_resource *resource = NULL; struct acpi_power_resource *resource = NULL;
struct list_head *node, *next;
struct acpi_power_reference *ref;
resource = seq->private; resource = seq->private;
...@@ -446,6 +494,10 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset) ...@@ -446,6 +494,10 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset)
if (!resource) if (!resource)
goto end; goto end;
result = acpi_power_get_state(resource);
if (result)
goto end;
seq_puts(seq, "state: "); seq_puts(seq, "state: ");
switch (resource->state) { switch (resource->state) {
case ACPI_POWER_RESOURCE_STATE_ON: case ACPI_POWER_RESOURCE_STATE_ON:
...@@ -459,11 +511,18 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset) ...@@ -459,11 +511,18 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset)
break; break;
} }
mutex_lock(&resource->resource_lock);
list_for_each_safe(node, next, &resource->reference) {
ref = container_of(node, struct acpi_power_reference, node);
count++;
}
mutex_unlock(&resource->resource_lock);
seq_printf(seq, "system level: S%d\n" seq_printf(seq, "system level: S%d\n"
"order: %d\n" "order: %d\n"
"reference count: %d\n", "reference count: %d\n",
resource->system_level, resource->system_level,
resource->order, resource->references); resource->order, count);
end: end:
return 0; return 0;
...@@ -536,6 +595,8 @@ static int acpi_power_add(struct acpi_device *device) ...@@ -536,6 +595,8 @@ static int acpi_power_add(struct acpi_device *device)
return -ENOMEM; return -ENOMEM;
resource->device = device; resource->device = device;
mutex_init(&resource->resource_lock);
INIT_LIST_HEAD(&resource->reference);
strcpy(resource->name, device->pnp.bus_id); strcpy(resource->name, device->pnp.bus_id);
strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_POWER_CLASS); strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
...@@ -583,6 +644,7 @@ static int acpi_power_add(struct acpi_device *device) ...@@ -583,6 +644,7 @@ static int acpi_power_add(struct acpi_device *device)
static int acpi_power_remove(struct acpi_device *device, int type) static int acpi_power_remove(struct acpi_device *device, int type)
{ {
struct acpi_power_resource *resource = NULL; struct acpi_power_resource *resource = NULL;
struct list_head *node, *next;
if (!device || !acpi_driver_data(device)) if (!device || !acpi_driver_data(device))
...@@ -592,11 +654,54 @@ static int acpi_power_remove(struct acpi_device *device, int type) ...@@ -592,11 +654,54 @@ static int acpi_power_remove(struct acpi_device *device, int type)
acpi_power_remove_fs(device); acpi_power_remove_fs(device);
mutex_lock(&resource->resource_lock);
list_for_each_safe(node, next, &resource->reference) {
struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node);
list_del(&ref->node);
kfree(ref);
}
mutex_unlock(&resource->resource_lock);
kfree(resource); kfree(resource);
return 0; return 0;
} }
static int acpi_power_resume(struct acpi_device *device)
{
int result = 0;
struct acpi_power_resource *resource = NULL;
struct acpi_power_reference *ref;
if (!device || !acpi_driver_data(device))
return -EINVAL;
resource = (struct acpi_power_resource *)acpi_driver_data(device);
result = acpi_power_get_state(resource);
if (result)
return result;
mutex_lock(&resource->resource_lock);
if ((resource->state == ACPI_POWER_RESOURCE_STATE_ON) &&
list_empty(&resource->reference)) {
mutex_unlock(&resource->resource_lock);
result = acpi_power_off_device(device->handle, NULL);
return result;
}
if ((resource->state == ACPI_POWER_RESOURCE_STATE_OFF) &&
!list_empty(&resource->reference)) {
ref = container_of(resource->reference.next, struct acpi_power_reference, node);
mutex_unlock(&resource->resource_lock);
result = acpi_power_on(device->handle, ref->device);
return result;
}
mutex_unlock(&resource->resource_lock);
return 0;
}
static int __init acpi_power_init(void) static int __init acpi_power_init(void)
{ {
int result = 0; int result = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment