Commit 34bdb1a4 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

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()
parents 0ad4991c 41a2a466
What: /sys/devices/.../power_resources_wakeup/
Date: April 2013
Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
The /sys/devices/.../power_resources_wakeup/ directory is only
present for device objects representing ACPI device nodes that
require ACPI power resources for wakeup signaling.
If present, it contains symbolic links to device directories
representing ACPI power resources that need to be turned on for
the given device node to be able to signal wakeup. The names of
the links are the same as the names of the directories they
point to.
......@@ -145,27 +145,36 @@ int acpi_device_get_power(struct acpi_device *device, int *state)
}
/*
* Get the device's power state either directly (via _PSC) or
* indirectly (via power resources).
* Get the device's power state from power resources settings and _PSC,
* if available.
*/
if (device->power.flags.power_resources) {
int error = acpi_power_get_inferred_state(device, &result);
if (error)
return error;
}
if (device->power.flags.explicit_get) {
acpi_handle handle = device->handle;
unsigned long long psc;
acpi_status status = acpi_evaluate_integer(device->handle,
"_PSC", NULL, &psc);
acpi_status status;
status = acpi_evaluate_integer(handle, "_PSC", NULL, &psc);
if (ACPI_FAILURE(status))
return -ENODEV;
result = psc;
}
/* The test below covers ACPI_STATE_UNKNOWN too. */
if (result <= ACPI_STATE_D2) {
; /* Do nothing. */
} else if (device->power.flags.power_resources) {
int error = acpi_power_get_inferred_state(device, &result);
if (error)
return error;
} else if (result == ACPI_STATE_D3_HOT) {
result = ACPI_STATE_D3;
/*
* The power resources settings may indicate a power state
* shallower than the actual power state of the device.
*
* Moreover, on systems predating ACPI 4.0, if the device
* doesn't depend on any power resources and _PSC returns 3,
* that means "power off". We need to maintain compatibility
* with those systems.
*/
if (psc > result && psc < ACPI_STATE_D3_COLD)
result = psc;
else if (result == ACPI_STATE_UNKNOWN)
result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc;
}
/*
......
......@@ -459,57 +459,79 @@ static struct attribute_group attr_groups[] = {
},
};
static void acpi_power_hide_list(struct acpi_device *adev, int state)
static struct attribute_group wakeup_attr_group = {
.name = "power_resources_wakeup",
.attrs = attrs,
};
static void acpi_power_hide_list(struct acpi_device *adev,
struct list_head *resources,
struct attribute_group *attr_group)
{
struct acpi_device_power_state *ps = &adev->power.states[state];
struct acpi_power_resource_entry *entry;
if (list_empty(&ps->resources))
if (list_empty(resources))
return;
list_for_each_entry_reverse(entry, &ps->resources, node) {
list_for_each_entry_reverse(entry, resources, node) {
struct acpi_device *res_dev = &entry->resource->device;
sysfs_remove_link_from_group(&adev->dev.kobj,
attr_groups[state].name,
attr_group->name,
dev_name(&res_dev->dev));
}
sysfs_remove_group(&adev->dev.kobj, &attr_groups[state]);
sysfs_remove_group(&adev->dev.kobj, attr_group);
}
static void acpi_power_expose_list(struct acpi_device *adev, int state)
static void acpi_power_expose_list(struct acpi_device *adev,
struct list_head *resources,
struct attribute_group *attr_group)
{
struct acpi_device_power_state *ps = &adev->power.states[state];
struct acpi_power_resource_entry *entry;
int ret;
if (list_empty(&ps->resources))
if (list_empty(resources))
return;
ret = sysfs_create_group(&adev->dev.kobj, &attr_groups[state]);
ret = sysfs_create_group(&adev->dev.kobj, attr_group);
if (ret)
return;
list_for_each_entry(entry, &ps->resources, node) {
list_for_each_entry(entry, resources, node) {
struct acpi_device *res_dev = &entry->resource->device;
ret = sysfs_add_link_to_group(&adev->dev.kobj,
attr_groups[state].name,
attr_group->name,
&res_dev->dev.kobj,
dev_name(&res_dev->dev));
if (ret) {
acpi_power_hide_list(adev, state);
acpi_power_hide_list(adev, resources, attr_group);
break;
}
}
}
static void acpi_power_expose_hide(struct acpi_device *adev,
struct list_head *resources,
struct attribute_group *attr_group,
bool expose)
{
if (expose)
acpi_power_expose_list(adev, resources, attr_group);
else
acpi_power_hide_list(adev, resources, attr_group);
}
void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
{
struct acpi_device_power_state *ps;
struct acpi_power_resource_entry *entry;
int state;
if (adev->wakeup.flags.valid)
acpi_power_expose_hide(adev, &adev->wakeup.resources,
&wakeup_attr_group, add);
if (!adev->power.flags.power_resources)
return;
......@@ -523,12 +545,10 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
acpi_power_remove_dependent(resource, adev);
}
for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) {
if (add)
acpi_power_expose_list(adev, state);
else
acpi_power_hide_list(adev, state);
}
for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++)
acpi_power_expose_hide(adev,
&adev->power.states[state].resources,
&attr_groups[state], add);
}
int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment