aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-04-27 19:54:00 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-04-27 19:54:00 -0400
commit34bdb1a458baaf3e56843c36deb04283c26a8099 (patch)
tree353acc5fb0aafecb81a14d0c3c970cc39b81af9a
parent0ad4991cae47c0d3ae93e1531ba5572d223d700c (diff)
parent41a2a4665d401f79912a3da83ff9ef3e502bc12e (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_wakeup13
-rw-r--r--drivers/acpi/device_pm.c39
-rw-r--r--drivers/acpi/power.c58
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 @@
1What: /sys/devices/.../power_resources_wakeup/
2Date: April 2013
3Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
4Description:
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
462static void acpi_power_hide_list(struct acpi_device *adev, int state) 462static struct attribute_group wakeup_attr_group = {
463 .name = "power_resources_wakeup",
464 .attrs = attrs,
465};
466
467static 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
480static void acpi_power_expose_list(struct acpi_device *adev, int state) 486static 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
514static 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
507void acpi_power_add_remove_device(struct acpi_device *adev, bool add) 525void 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
534int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) 554int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p)