aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r--drivers/acpi/power.c147
1 files changed, 126 insertions, 21 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 92f80ba491c5..00d6118ff1ef 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -56,6 +56,7 @@ ACPI_MODULE_NAME("power");
56#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF 56#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
57static int acpi_power_add(struct acpi_device *device); 57static int acpi_power_add(struct acpi_device *device);
58static int acpi_power_remove(struct acpi_device *device, int type); 58static int acpi_power_remove(struct acpi_device *device, int type);
59static int acpi_power_resume(struct acpi_device *device);
59static int acpi_power_open_fs(struct inode *inode, struct file *file); 60static int acpi_power_open_fs(struct inode *inode, struct file *file);
60 61
61static struct acpi_driver acpi_power_driver = { 62static struct acpi_driver acpi_power_driver = {
@@ -65,16 +66,23 @@ static struct acpi_driver acpi_power_driver = {
65 .ops = { 66 .ops = {
66 .add = acpi_power_add, 67 .add = acpi_power_add,
67 .remove = acpi_power_remove, 68 .remove = acpi_power_remove,
69 .resume = acpi_power_resume,
68 }, 70 },
69}; 71};
70 72
73struct acpi_power_reference {
74 struct list_head node;
75 struct acpi_device *device;
76};
77
71struct acpi_power_resource { 78struct acpi_power_resource {
72 struct acpi_device * device; 79 struct acpi_device * device;
73 acpi_bus_id name; 80 acpi_bus_id name;
74 u32 system_level; 81 u32 system_level;
75 u32 order; 82 u32 order;
76 int state; 83 int state;
77 int references; 84 struct mutex resource_lock;
85 struct list_head reference;
78}; 86};
79 87
80static struct list_head acpi_power_resource_list; 88static 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 return result; 178 return result;
171} 179}
172 180
173static int acpi_power_on(acpi_handle handle) 181static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
174{ 182{
175 int result = 0; 183 int result = 0;
184 int found = 0;
176 acpi_status status = AE_OK; 185 acpi_status status = AE_OK;
177 struct acpi_device *device = NULL;
178 struct acpi_power_resource *resource = NULL; 186 struct acpi_power_resource *resource = NULL;
187 struct list_head *node, *next;
188 struct acpi_power_reference *ref;
179 189
180 190
181 result = acpi_power_get_context(handle, &resource); 191 result = acpi_power_get_context(handle, &resource);
182 if (result) 192 if (result)
183 return result; 193 return result;
184 194
185 resource->references++; 195 mutex_lock(&resource->resource_lock);
196 list_for_each_safe(node, next, &resource->reference) {
197 ref = container_of(node, struct acpi_power_reference, node);
198 if (dev->handle == ref->device->handle) {
199 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n",
200 dev->pnp.bus_id, resource->name));
201 found = 1;
202 break;
203 }
204 }
205
206 if (!found) {
207 ref = kmalloc(sizeof (struct acpi_power_reference),
208 irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
209 if (!ref) {
210 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n"));
211 mutex_unlock(&resource->resource_lock);
212 return -ENOMEM;
213 }
214 list_add_tail(&ref->node, &resource->reference);
215 ref->device = dev;
216 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n",
217 dev->pnp.bus_id, resource->name));
218 }
219 mutex_unlock(&resource->resource_lock);
186 220
187 if ((resource->references > 1) 221 if (resource->state == ACPI_POWER_RESOURCE_STATE_ON) {
188 || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
189 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n", 222 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
190 resource->name)); 223 resource->name));
191 return 0; 224 return 0;
@@ -202,38 +235,49 @@ static int acpi_power_on(acpi_handle handle)
202 return -ENOEXEC; 235 return -ENOEXEC;
203 236
204 /* Update the power resource's _device_ power state */ 237 /* Update the power resource's _device_ power state */
205 device = resource->device;
206 resource->device->power.state = ACPI_STATE_D0; 238 resource->device->power.state = ACPI_STATE_D0;
207 239
208 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", 240 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
209 resource->name)); 241 resource->name));
210
211 return 0; 242 return 0;
212} 243}
213 244
214static int acpi_power_off_device(acpi_handle handle) 245static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
215{ 246{
216 int result = 0; 247 int result = 0;
217 acpi_status status = AE_OK; 248 acpi_status status = AE_OK;
218 struct acpi_power_resource *resource = NULL; 249 struct acpi_power_resource *resource = NULL;
250 struct list_head *node, *next;
251 struct acpi_power_reference *ref;
252
219 253
220 result = acpi_power_get_context(handle, &resource); 254 result = acpi_power_get_context(handle, &resource);
221 if (result) 255 if (result)
222 return result; 256 return result;
223 257
224 if (resource->references) 258 mutex_lock(&resource->resource_lock);
225 resource->references--; 259 list_for_each_safe(node, next, &resource->reference) {
260 ref = container_of(node, struct acpi_power_reference, node);
261 if (dev->handle == ref->device->handle) {
262 list_del(&ref->node);
263 kfree(ref);
264 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n",
265 dev->pnp.bus_id, resource->name));
266 break;
267 }
268 }
226 269
227 if (resource->references) { 270 if (!list_empty(&resource->reference)) {
228 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 271 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n",
229 "Resource [%s] is still in use, dereferencing\n", 272 resource->name));
230 resource->device->pnp.bus_id)); 273 mutex_unlock(&resource->resource_lock);
231 return 0; 274 return 0;
232 } 275 }
276 mutex_unlock(&resource->resource_lock);
233 277
234 if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { 278 if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
235 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", 279 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
236 resource->device->pnp.bus_id)); 280 resource->name));
237 return 0; 281 return 0;
238 } 282 }
239 283
@@ -275,7 +319,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev)
275 arg.integer.value = 1; 319 arg.integer.value = 1;
276 /* Open power resource */ 320 /* Open power resource */
277 for (i = 0; i < dev->wakeup.resources.count; i++) { 321 for (i = 0; i < dev->wakeup.resources.count; i++) {
278 ret = acpi_power_on(dev->wakeup.resources.handles[i]); 322 ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
279 if (ret) { 323 if (ret) {
280 printk(KERN_ERR PREFIX "Transition power state\n"); 324 printk(KERN_ERR PREFIX "Transition power state\n");
281 dev->wakeup.flags.valid = 0; 325 dev->wakeup.flags.valid = 0;
@@ -322,7 +366,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
322 366
323 /* Close power resource */ 367 /* Close power resource */
324 for (i = 0; i < dev->wakeup.resources.count; i++) { 368 for (i = 0; i < dev->wakeup.resources.count; i++) {
325 ret = acpi_power_off_device(dev->wakeup.resources.handles[i]); 369 ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev);
326 if (ret) { 370 if (ret) {
327 printk(KERN_ERR PREFIX "Transition power state\n"); 371 printk(KERN_ERR PREFIX "Transition power state\n");
328 dev->wakeup.flags.valid = 0; 372 dev->wakeup.flags.valid = 0;
@@ -406,7 +450,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
406 * (e.g. so the device doesn't lose power while transitioning). 450 * (e.g. so the device doesn't lose power while transitioning).
407 */ 451 */
408 for (i = 0; i < tl->count; i++) { 452 for (i = 0; i < tl->count; i++) {
409 result = acpi_power_on(tl->handles[i]); 453 result = acpi_power_on(tl->handles[i], device);
410 if (result) 454 if (result)
411 goto end; 455 goto end;
412 } 456 }
@@ -415,7 +459,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
415 * Then we dereference all power resources used in the current list. 459 * Then we dereference all power resources used in the current list.
416 */ 460 */
417 for (i = 0; i < cl->count; i++) { 461 for (i = 0; i < cl->count; i++) {
418 result = acpi_power_off_device(cl->handles[i]); 462 result = acpi_power_off_device(cl->handles[i], device);
419 if (result) 463 if (result)
420 goto end; 464 goto end;
421 } 465 }
@@ -438,7 +482,11 @@ static struct proc_dir_entry *acpi_power_dir;
438 482
439static int acpi_power_seq_show(struct seq_file *seq, void *offset) 483static int acpi_power_seq_show(struct seq_file *seq, void *offset)
440{ 484{
485 int count = 0;
486 int result = 0;
441 struct acpi_power_resource *resource = NULL; 487 struct acpi_power_resource *resource = NULL;
488 struct list_head *node, *next;
489 struct acpi_power_reference *ref;
442 490
443 491
444 resource = seq->private; 492 resource = seq->private;
@@ -446,6 +494,10 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset)
446 if (!resource) 494 if (!resource)
447 goto end; 495 goto end;
448 496
497 result = acpi_power_get_state(resource);
498 if (result)
499 goto end;
500
449 seq_puts(seq, "state: "); 501 seq_puts(seq, "state: ");
450 switch (resource->state) { 502 switch (resource->state) {
451 case ACPI_POWER_RESOURCE_STATE_ON: 503 case ACPI_POWER_RESOURCE_STATE_ON:
@@ -459,11 +511,18 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset)
459 break; 511 break;
460 } 512 }
461 513
514 mutex_lock(&resource->resource_lock);
515 list_for_each_safe(node, next, &resource->reference) {
516 ref = container_of(node, struct acpi_power_reference, node);
517 count++;
518 }
519 mutex_unlock(&resource->resource_lock);
520
462 seq_printf(seq, "system level: S%d\n" 521 seq_printf(seq, "system level: S%d\n"
463 "order: %d\n" 522 "order: %d\n"
464 "reference count: %d\n", 523 "reference count: %d\n",
465 resource->system_level, 524 resource->system_level,
466 resource->order, resource->references); 525 resource->order, count);
467 526
468 end: 527 end:
469 return 0; 528 return 0;
@@ -536,6 +595,8 @@ static int acpi_power_add(struct acpi_device *device)
536 return -ENOMEM; 595 return -ENOMEM;
537 596
538 resource->device = device; 597 resource->device = device;
598 mutex_init(&resource->resource_lock);
599 INIT_LIST_HEAD(&resource->reference);
539 strcpy(resource->name, device->pnp.bus_id); 600 strcpy(resource->name, device->pnp.bus_id);
540 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); 601 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
541 strcpy(acpi_device_class(device), ACPI_POWER_CLASS); 602 strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
@@ -583,6 +644,7 @@ static int acpi_power_add(struct acpi_device *device)
583static int acpi_power_remove(struct acpi_device *device, int type) 644static int acpi_power_remove(struct acpi_device *device, int type)
584{ 645{
585 struct acpi_power_resource *resource = NULL; 646 struct acpi_power_resource *resource = NULL;
647 struct list_head *node, *next;
586 648
587 649
588 if (!device || !acpi_driver_data(device)) 650 if (!device || !acpi_driver_data(device))
@@ -592,11 +654,54 @@ static int acpi_power_remove(struct acpi_device *device, int type)
592 654
593 acpi_power_remove_fs(device); 655 acpi_power_remove_fs(device);
594 656
657 mutex_lock(&resource->resource_lock);
658 list_for_each_safe(node, next, &resource->reference) {
659 struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node);
660 list_del(&ref->node);
661 kfree(ref);
662 }
663 mutex_unlock(&resource->resource_lock);
664
595 kfree(resource); 665 kfree(resource);
596 666
597 return 0; 667 return 0;
598} 668}
599 669
670static int acpi_power_resume(struct acpi_device *device)
671{
672 int result = 0;
673 struct acpi_power_resource *resource = NULL;
674 struct acpi_power_reference *ref;
675
676 if (!device || !acpi_driver_data(device))
677 return -EINVAL;
678
679 resource = (struct acpi_power_resource *)acpi_driver_data(device);
680
681 result = acpi_power_get_state(resource);
682 if (result)
683 return result;
684
685 mutex_lock(&resource->resource_lock);
686 if ((resource->state == ACPI_POWER_RESOURCE_STATE_ON) &&
687 list_empty(&resource->reference)) {
688 mutex_unlock(&resource->resource_lock);
689 result = acpi_power_off_device(device->handle, NULL);
690 return result;
691 }
692
693 if ((resource->state == ACPI_POWER_RESOURCE_STATE_OFF) &&
694 !list_empty(&resource->reference)) {
695 ref = container_of(resource->reference.next, struct acpi_power_reference, node);
696 mutex_unlock(&resource->resource_lock);
697 result = acpi_power_on(device->handle, ref->device);
698 return result;
699 }
700
701 mutex_unlock(&resource->resource_lock);
702 return 0;
703}
704
600static int __init acpi_power_init(void) 705static int __init acpi_power_init(void)
601{ 706{
602 int result = 0; 707 int result = 0;