diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-04-27 19:54:00 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-04-27 19:54:00 -0400 |
commit | 34bdb1a458baaf3e56843c36deb04283c26a8099 (patch) | |
tree | 353acc5fb0aafecb81a14d0c3c970cc39b81af9a | |
parent | 0ad4991cae47c0d3ae93e1531ba5572d223d700c (diff) | |
parent | 41a2a4665d401f79912a3da83ff9ef3e502bc12e (diff) |
Merge branch 'acpi-pm'
* acpi-pm:
ACPI / PM: Expose lists of device wakeup power resources to user space
ACPI / PM: Fix potential problem in acpi_device_get_power()
-rw-r--r-- | Documentation/ABI/testing/sysfs-devices-power_resources_wakeup | 13 | ||||
-rw-r--r-- | drivers/acpi/device_pm.c | 39 | ||||
-rw-r--r-- | drivers/acpi/power.c | 58 |
3 files changed, 76 insertions, 34 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-power_resources_wakeup b/Documentation/ABI/testing/sysfs-devices-power_resources_wakeup new file mode 100644 index 000000000000..e0588feeb6e1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-power_resources_wakeup | |||
@@ -0,0 +1,13 @@ | |||
1 | What: /sys/devices/.../power_resources_wakeup/ | ||
2 | Date: April 2013 | ||
3 | Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com> | ||
4 | Description: | ||
5 | The /sys/devices/.../power_resources_wakeup/ directory is only | ||
6 | present for device objects representing ACPI device nodes that | ||
7 | require ACPI power resources for wakeup signaling. | ||
8 | |||
9 | If present, it contains symbolic links to device directories | ||
10 | representing ACPI power resources that need to be turned on for | ||
11 | the given device node to be able to signal wakeup. The names of | ||
12 | the links are the same as the names of the directories they | ||
13 | point to. | ||
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index dd314ef9bff1..96de787e6104 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -145,27 +145,36 @@ int acpi_device_get_power(struct acpi_device *device, int *state) | |||
145 | } | 145 | } |
146 | 146 | ||
147 | /* | 147 | /* |
148 | * Get the device's power state either directly (via _PSC) or | 148 | * Get the device's power state from power resources settings and _PSC, |
149 | * indirectly (via power resources). | 149 | * if available. |
150 | */ | 150 | */ |
151 | if (device->power.flags.power_resources) { | ||
152 | int error = acpi_power_get_inferred_state(device, &result); | ||
153 | if (error) | ||
154 | return error; | ||
155 | } | ||
151 | if (device->power.flags.explicit_get) { | 156 | if (device->power.flags.explicit_get) { |
157 | acpi_handle handle = device->handle; | ||
152 | unsigned long long psc; | 158 | unsigned long long psc; |
153 | acpi_status status = acpi_evaluate_integer(device->handle, | 159 | acpi_status status; |
154 | "_PSC", NULL, &psc); | 160 | |
161 | status = acpi_evaluate_integer(handle, "_PSC", NULL, &psc); | ||
155 | if (ACPI_FAILURE(status)) | 162 | if (ACPI_FAILURE(status)) |
156 | return -ENODEV; | 163 | return -ENODEV; |
157 | 164 | ||
158 | result = psc; | 165 | /* |
159 | } | 166 | * The power resources settings may indicate a power state |
160 | /* The test below covers ACPI_STATE_UNKNOWN too. */ | 167 | * shallower than the actual power state of the device. |
161 | if (result <= ACPI_STATE_D2) { | 168 | * |
162 | ; /* Do nothing. */ | 169 | * Moreover, on systems predating ACPI 4.0, if the device |
163 | } else if (device->power.flags.power_resources) { | 170 | * doesn't depend on any power resources and _PSC returns 3, |
164 | int error = acpi_power_get_inferred_state(device, &result); | 171 | * that means "power off". We need to maintain compatibility |
165 | if (error) | 172 | * with those systems. |
166 | return error; | 173 | */ |
167 | } else if (result == ACPI_STATE_D3_HOT) { | 174 | if (psc > result && psc < ACPI_STATE_D3_COLD) |
168 | result = ACPI_STATE_D3; | 175 | result = psc; |
176 | else if (result == ACPI_STATE_UNKNOWN) | ||
177 | result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc; | ||
169 | } | 178 | } |
170 | 179 | ||
171 | /* | 180 | /* |
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 0481b1b1d2aa..f962047c6c85 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c | |||
@@ -459,57 +459,79 @@ static struct attribute_group attr_groups[] = { | |||
459 | }, | 459 | }, |
460 | }; | 460 | }; |
461 | 461 | ||
462 | static void acpi_power_hide_list(struct acpi_device *adev, int state) | 462 | static struct attribute_group wakeup_attr_group = { |
463 | .name = "power_resources_wakeup", | ||
464 | .attrs = attrs, | ||
465 | }; | ||
466 | |||
467 | static void acpi_power_hide_list(struct acpi_device *adev, | ||
468 | struct list_head *resources, | ||
469 | struct attribute_group *attr_group) | ||
463 | { | 470 | { |
464 | struct acpi_device_power_state *ps = &adev->power.states[state]; | ||
465 | struct acpi_power_resource_entry *entry; | 471 | struct acpi_power_resource_entry *entry; |
466 | 472 | ||
467 | if (list_empty(&ps->resources)) | 473 | if (list_empty(resources)) |
468 | return; | 474 | return; |
469 | 475 | ||
470 | list_for_each_entry_reverse(entry, &ps->resources, node) { | 476 | list_for_each_entry_reverse(entry, resources, node) { |
471 | struct acpi_device *res_dev = &entry->resource->device; | 477 | struct acpi_device *res_dev = &entry->resource->device; |
472 | 478 | ||
473 | sysfs_remove_link_from_group(&adev->dev.kobj, | 479 | sysfs_remove_link_from_group(&adev->dev.kobj, |
474 | attr_groups[state].name, | 480 | attr_group->name, |
475 | dev_name(&res_dev->dev)); | 481 | dev_name(&res_dev->dev)); |
476 | } | 482 | } |
477 | sysfs_remove_group(&adev->dev.kobj, &attr_groups[state]); | 483 | sysfs_remove_group(&adev->dev.kobj, attr_group); |
478 | } | 484 | } |
479 | 485 | ||
480 | static void acpi_power_expose_list(struct acpi_device *adev, int state) | 486 | static void acpi_power_expose_list(struct acpi_device *adev, |
487 | struct list_head *resources, | ||
488 | struct attribute_group *attr_group) | ||
481 | { | 489 | { |
482 | struct acpi_device_power_state *ps = &adev->power.states[state]; | ||
483 | struct acpi_power_resource_entry *entry; | 490 | struct acpi_power_resource_entry *entry; |
484 | int ret; | 491 | int ret; |
485 | 492 | ||
486 | if (list_empty(&ps->resources)) | 493 | if (list_empty(resources)) |
487 | return; | 494 | return; |
488 | 495 | ||
489 | ret = sysfs_create_group(&adev->dev.kobj, &attr_groups[state]); | 496 | ret = sysfs_create_group(&adev->dev.kobj, attr_group); |
490 | if (ret) | 497 | if (ret) |
491 | return; | 498 | return; |
492 | 499 | ||
493 | list_for_each_entry(entry, &ps->resources, node) { | 500 | list_for_each_entry(entry, resources, node) { |
494 | struct acpi_device *res_dev = &entry->resource->device; | 501 | struct acpi_device *res_dev = &entry->resource->device; |
495 | 502 | ||
496 | ret = sysfs_add_link_to_group(&adev->dev.kobj, | 503 | ret = sysfs_add_link_to_group(&adev->dev.kobj, |
497 | attr_groups[state].name, | 504 | attr_group->name, |
498 | &res_dev->dev.kobj, | 505 | &res_dev->dev.kobj, |
499 | dev_name(&res_dev->dev)); | 506 | dev_name(&res_dev->dev)); |
500 | if (ret) { | 507 | if (ret) { |
501 | acpi_power_hide_list(adev, state); | 508 | acpi_power_hide_list(adev, resources, attr_group); |
502 | break; | 509 | break; |
503 | } | 510 | } |
504 | } | 511 | } |
505 | } | 512 | } |
506 | 513 | ||
514 | static void acpi_power_expose_hide(struct acpi_device *adev, | ||
515 | struct list_head *resources, | ||
516 | struct attribute_group *attr_group, | ||
517 | bool expose) | ||
518 | { | ||
519 | if (expose) | ||
520 | acpi_power_expose_list(adev, resources, attr_group); | ||
521 | else | ||
522 | acpi_power_hide_list(adev, resources, attr_group); | ||
523 | } | ||
524 | |||
507 | void acpi_power_add_remove_device(struct acpi_device *adev, bool add) | 525 | void acpi_power_add_remove_device(struct acpi_device *adev, bool add) |
508 | { | 526 | { |
509 | struct acpi_device_power_state *ps; | 527 | struct acpi_device_power_state *ps; |
510 | struct acpi_power_resource_entry *entry; | 528 | struct acpi_power_resource_entry *entry; |
511 | int state; | 529 | int state; |
512 | 530 | ||
531 | if (adev->wakeup.flags.valid) | ||
532 | acpi_power_expose_hide(adev, &adev->wakeup.resources, | ||
533 | &wakeup_attr_group, add); | ||
534 | |||
513 | if (!adev->power.flags.power_resources) | 535 | if (!adev->power.flags.power_resources) |
514 | return; | 536 | return; |
515 | 537 | ||
@@ -523,12 +545,10 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add) | |||
523 | acpi_power_remove_dependent(resource, adev); | 545 | acpi_power_remove_dependent(resource, adev); |
524 | } | 546 | } |
525 | 547 | ||
526 | for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) { | 548 | for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) |
527 | if (add) | 549 | acpi_power_expose_hide(adev, |
528 | acpi_power_expose_list(adev, state); | 550 | &adev->power.states[state].resources, |
529 | else | 551 | &attr_groups[state], add); |
530 | acpi_power_hide_list(adev, state); | ||
531 | } | ||
532 | } | 552 | } |
533 | 553 | ||
534 | int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) | 554 | int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) |