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()
This commit is contained in:
Rafael J. Wysocki 2013-04-28 01:54:00 +02:00
commit 34bdb1a458
3 changed files with 76 additions and 34 deletions

View file

@ -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.explicit_get) {
unsigned long long psc;
acpi_status status = acpi_evaluate_integer(device->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) {
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;
}
if (device->power.flags.explicit_get) {
acpi_handle handle = device->handle;
unsigned long long psc;
acpi_status status;
status = acpi_evaluate_integer(handle, "_PSC", NULL, &psc);
if (ACPI_FAILURE(status))
return -ENODEV;
/*
* 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;
}
/*

View file

@ -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)