Pull bugzilla-7122 into release branch
This commit is contained in:
commit
1004879f25
@ -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 = {
|
|||||||
.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)
|
|||||||
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 ((resource->references > 1)
|
if (!found) {
|
||||||
|| (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
|
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->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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
/* 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)
|
|||||||
* (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)
|
|||||||
* 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;
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user