aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-02-23 17:15:21 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-02-23 17:15:21 -0500
commitb5d667eb392ed901fc7ae76869c7a130559e193c (patch)
tree25824b7ac66bd8c3586499e4b38aabae2e0665e5 /drivers/acpi
parent511d5c4212948fe55035b8fed61ac0e125af5a05 (diff)
ACPI / PM: Take unusual configurations of power resources into account
Commit d2e5f0c (ACPI / PCI: Rework the setup and cleanup of device wakeup) moved the initial disabling of system wakeup for PCI devices into a place where it can actually work and that exposed a hidden old issue with crap^Wunusual system designs where the same power resources are used for both wakeup power and device power control at run time. Namely, say there is one power resource such that the ACPI power state D0 of a PCI device depends on that power resource (i.e. the device is in D0 when that power resource is "on") and it is used as a wakeup power resource for the same device. Then, calling acpi_pci_sleep_wake(pci_dev, false) for the device in question will cause the reference counter of that power resource to drop to 0, which in turn will cause it to be turned off. As a result, the device will go into D3cold at that point, although it should have stayed in D0. As it turns out, that happens to USB controllers on some laptops and USB becomes unusable on those machines as a result, which is a major regression from v3.8. To fix this problem, (1) increment the reference counters of wakup power resources during their initialization if they are "on" initially, (2) prevent acpi_disable_wakeup_device_power() from decrementing the reference counters of wakeup power resources that were not enabled for wakeup power previously, and (3) prevent acpi_enable_wakeup_device_power() from incrementing the reference counters of wakeup power resources that already are enabled for wakeup power. In addition to that, if it is impossible to determine the initial states of wakeup power resources, avoid enabling wakeup for devices whose wakeup power depends on those power resources. Reported-by: Dave Jones <davej@redhat.com> Reported-by: Fabio Baltieri <fabio.baltieri@linaro.org> Tested-by: Fabio Baltieri <fabio.baltieri@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/internal.h2
-rw-r--r--drivers/acpi/power.c112
-rw-r--r--drivers/acpi/scan.c9
3 files changed, 94 insertions, 29 deletions
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index c5a61cd6c1a5..6306d2ecb428 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -56,7 +56,7 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
56 struct list_head *list); 56 struct list_head *list);
57int acpi_add_power_resource(acpi_handle handle); 57int acpi_add_power_resource(acpi_handle handle);
58void acpi_power_add_remove_device(struct acpi_device *adev, bool add); 58void acpi_power_add_remove_device(struct acpi_device *adev, bool add);
59int acpi_power_min_system_level(struct list_head *list); 59int acpi_power_wakeup_list_init(struct list_head *list, int *system_level);
60int acpi_device_sleep_wake(struct acpi_device *dev, 60int acpi_device_sleep_wake(struct acpi_device *dev,
61 int enable, int sleep_state, int dev_state); 61 int enable, int sleep_state, int dev_state);
62int acpi_power_get_inferred_state(struct acpi_device *device, int *state); 62int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
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
275static int acpi_power_on(struct acpi_power_resource *resource) 276static 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); 298static 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
316static int acpi_power_off(struct acpi_power_resource *resource) 322static 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: 345static 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
524int acpi_power_min_system_level(struct list_head *list) 534int 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 */
611int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) 638int 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 */
648int acpi_disable_wakeup_device_power(struct acpi_device *dev) 690int 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:
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index f75f25c2e455..560b05566f3b 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1004,7 +1004,14 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
1004 if (!list_empty(&wakeup->resources)) { 1004 if (!list_empty(&wakeup->resources)) {
1005 int sleep_state; 1005 int sleep_state;
1006 1006
1007 sleep_state = acpi_power_min_system_level(&wakeup->resources); 1007 err = acpi_power_wakeup_list_init(&wakeup->resources,
1008 &sleep_state);
1009 if (err) {
1010 acpi_handle_warn(handle, "Retrieving current states "
1011 "of wakeup power resources failed\n");
1012 acpi_power_resources_list_free(&wakeup->resources);
1013 goto out;
1014 }
1008 if (sleep_state < wakeup->sleep_state) { 1015 if (sleep_state < wakeup->sleep_state) {
1009 acpi_handle_warn(handle, "Overriding _PRW sleep state " 1016 acpi_handle_warn(handle, "Overriding _PRW sleep state "
1010 "(S%d) by S%d from power resources\n", 1017 "(S%d) by S%d from power resources\n",