diff options
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r-- | drivers/acpi/power.c | 112 |
1 files changed, 85 insertions, 27 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index b820528a5fa3..34f5ef11d427 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c | |||
@@ -73,6 +73,7 @@ struct acpi_power_resource { | |||
73 | u32 system_level; | 73 | u32 system_level; |
74 | u32 order; | 74 | u32 order; |
75 | unsigned int ref_count; | 75 | unsigned int ref_count; |
76 | bool wakeup_enabled; | ||
76 | struct mutex resource_lock; | 77 | struct mutex resource_lock; |
77 | }; | 78 | }; |
78 | 79 | ||
@@ -272,11 +273,9 @@ static int __acpi_power_on(struct acpi_power_resource *resource) | |||
272 | return 0; | 273 | return 0; |
273 | } | 274 | } |
274 | 275 | ||
275 | static int acpi_power_on(struct acpi_power_resource *resource) | 276 | static int acpi_power_on_unlocked(struct acpi_power_resource *resource) |
276 | { | 277 | { |
277 | int result = 0;; | 278 | int result = 0; |
278 | |||
279 | mutex_lock(&resource->resource_lock); | ||
280 | 279 | ||
281 | if (resource->ref_count++) { | 280 | if (resource->ref_count++) { |
282 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 281 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
@@ -293,9 +292,16 @@ static int acpi_power_on(struct acpi_power_resource *resource) | |||
293 | schedule_work(&dep->work); | 292 | schedule_work(&dep->work); |
294 | } | 293 | } |
295 | } | 294 | } |
295 | return result; | ||
296 | } | ||
296 | 297 | ||
297 | mutex_unlock(&resource->resource_lock); | 298 | static int acpi_power_on(struct acpi_power_resource *resource) |
299 | { | ||
300 | int result; | ||
298 | 301 | ||
302 | mutex_lock(&resource->resource_lock); | ||
303 | result = acpi_power_on_unlocked(resource); | ||
304 | mutex_unlock(&resource->resource_lock); | ||
299 | return result; | 305 | return result; |
300 | } | 306 | } |
301 | 307 | ||
@@ -313,17 +319,15 @@ static int __acpi_power_off(struct acpi_power_resource *resource) | |||
313 | return 0; | 319 | return 0; |
314 | } | 320 | } |
315 | 321 | ||
316 | static int acpi_power_off(struct acpi_power_resource *resource) | 322 | static int acpi_power_off_unlocked(struct acpi_power_resource *resource) |
317 | { | 323 | { |
318 | int result = 0; | 324 | int result = 0; |
319 | 325 | ||
320 | mutex_lock(&resource->resource_lock); | ||
321 | |||
322 | if (!resource->ref_count) { | 326 | if (!resource->ref_count) { |
323 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 327 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
324 | "Power resource [%s] already off", | 328 | "Power resource [%s] already off", |
325 | resource->name)); | 329 | resource->name)); |
326 | goto unlock; | 330 | return 0; |
327 | } | 331 | } |
328 | 332 | ||
329 | if (--resource->ref_count) { | 333 | if (--resource->ref_count) { |
@@ -335,10 +339,16 @@ static int acpi_power_off(struct acpi_power_resource *resource) | |||
335 | if (result) | 339 | if (result) |
336 | resource->ref_count++; | 340 | resource->ref_count++; |
337 | } | 341 | } |
342 | return result; | ||
343 | } | ||
338 | 344 | ||
339 | unlock: | 345 | static int acpi_power_off(struct acpi_power_resource *resource) |
340 | mutex_unlock(&resource->resource_lock); | 346 | { |
347 | int result; | ||
341 | 348 | ||
349 | mutex_lock(&resource->resource_lock); | ||
350 | result = acpi_power_off_unlocked(resource); | ||
351 | mutex_unlock(&resource->resource_lock); | ||
342 | return result; | 352 | return result; |
343 | } | 353 | } |
344 | 354 | ||
@@ -521,18 +531,35 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add) | |||
521 | } | 531 | } |
522 | } | 532 | } |
523 | 533 | ||
524 | int acpi_power_min_system_level(struct list_head *list) | 534 | int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) |
525 | { | 535 | { |
526 | struct acpi_power_resource_entry *entry; | 536 | struct acpi_power_resource_entry *entry; |
527 | int system_level = 5; | 537 | int system_level = 5; |
528 | 538 | ||
529 | list_for_each_entry(entry, list, node) { | 539 | list_for_each_entry(entry, list, node) { |
530 | struct acpi_power_resource *resource = entry->resource; | 540 | struct acpi_power_resource *resource = entry->resource; |
541 | acpi_handle handle = resource->device.handle; | ||
542 | int result; | ||
543 | int state; | ||
531 | 544 | ||
545 | mutex_lock(&resource->resource_lock); | ||
546 | |||
547 | result = acpi_power_get_state(handle, &state); | ||
548 | if (result) { | ||
549 | mutex_unlock(&resource->resource_lock); | ||
550 | return result; | ||
551 | } | ||
552 | if (state == ACPI_POWER_RESOURCE_STATE_ON) { | ||
553 | resource->ref_count++; | ||
554 | resource->wakeup_enabled = true; | ||
555 | } | ||
532 | if (system_level > resource->system_level) | 556 | if (system_level > resource->system_level) |
533 | system_level = resource->system_level; | 557 | system_level = resource->system_level; |
558 | |||
559 | mutex_unlock(&resource->resource_lock); | ||
534 | } | 560 | } |
535 | return system_level; | 561 | *system_level_p = system_level; |
562 | return 0; | ||
536 | } | 563 | } |
537 | 564 | ||
538 | /* -------------------------------------------------------------------------- | 565 | /* -------------------------------------------------------------------------- |
@@ -610,6 +637,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev, | |||
610 | */ | 637 | */ |
611 | int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) | 638 | int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) |
612 | { | 639 | { |
640 | struct acpi_power_resource_entry *entry; | ||
613 | int err = 0; | 641 | int err = 0; |
614 | 642 | ||
615 | if (!dev || !dev->wakeup.flags.valid) | 643 | if (!dev || !dev->wakeup.flags.valid) |
@@ -620,17 +648,31 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) | |||
620 | if (dev->wakeup.prepare_count++) | 648 | if (dev->wakeup.prepare_count++) |
621 | goto out; | 649 | goto out; |
622 | 650 | ||
623 | err = acpi_power_on_list(&dev->wakeup.resources); | 651 | list_for_each_entry(entry, &dev->wakeup.resources, node) { |
624 | if (err) { | 652 | struct acpi_power_resource *resource = entry->resource; |
625 | dev_err(&dev->dev, "Cannot turn wakeup power resources on\n"); | 653 | |
626 | dev->wakeup.flags.valid = 0; | 654 | mutex_lock(&resource->resource_lock); |
627 | } else { | 655 | |
628 | /* | 656 | if (!resource->wakeup_enabled) { |
629 | * Passing 3 as the third argument below means the device may be | 657 | err = acpi_power_on_unlocked(resource); |
630 | * put into arbitrary power state afterward. | 658 | if (!err) |
631 | */ | 659 | resource->wakeup_enabled = true; |
632 | err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); | 660 | } |
661 | |||
662 | mutex_unlock(&resource->resource_lock); | ||
663 | |||
664 | if (err) { | ||
665 | dev_err(&dev->dev, | ||
666 | "Cannot turn wakeup power resources on\n"); | ||
667 | dev->wakeup.flags.valid = 0; | ||
668 | goto out; | ||
669 | } | ||
633 | } | 670 | } |
671 | /* | ||
672 | * Passing 3 as the third argument below means the device may be | ||
673 | * put into arbitrary power state afterward. | ||
674 | */ | ||
675 | err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); | ||
634 | if (err) | 676 | if (err) |
635 | dev->wakeup.prepare_count = 0; | 677 | dev->wakeup.prepare_count = 0; |
636 | 678 | ||
@@ -647,6 +689,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) | |||
647 | */ | 689 | */ |
648 | int acpi_disable_wakeup_device_power(struct acpi_device *dev) | 690 | int acpi_disable_wakeup_device_power(struct acpi_device *dev) |
649 | { | 691 | { |
692 | struct acpi_power_resource_entry *entry; | ||
650 | int err = 0; | 693 | int err = 0; |
651 | 694 | ||
652 | if (!dev || !dev->wakeup.flags.valid) | 695 | if (!dev || !dev->wakeup.flags.valid) |
@@ -668,10 +711,25 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) | |||
668 | if (err) | 711 | if (err) |
669 | goto out; | 712 | goto out; |
670 | 713 | ||
671 | err = acpi_power_off_list(&dev->wakeup.resources); | 714 | list_for_each_entry(entry, &dev->wakeup.resources, node) { |
672 | if (err) { | 715 | struct acpi_power_resource *resource = entry->resource; |
673 | dev_err(&dev->dev, "Cannot turn wakeup power resources off\n"); | 716 | |
674 | dev->wakeup.flags.valid = 0; | 717 | mutex_lock(&resource->resource_lock); |
718 | |||
719 | if (resource->wakeup_enabled) { | ||
720 | err = acpi_power_off_unlocked(resource); | ||
721 | if (!err) | ||
722 | resource->wakeup_enabled = false; | ||
723 | } | ||
724 | |||
725 | mutex_unlock(&resource->resource_lock); | ||
726 | |||
727 | if (err) { | ||
728 | dev_err(&dev->dev, | ||
729 | "Cannot turn wakeup power resources off\n"); | ||
730 | dev->wakeup.flags.valid = 0; | ||
731 | break; | ||
732 | } | ||
675 | } | 733 | } |
676 | 734 | ||
677 | out: | 735 | out: |