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