Skip to content

Commit

Permalink
Merge branch 'acpi-pm'
Browse files Browse the repository at this point in the history
* acpi-pm:
  ACPI / PM: Expose lists of device wakeup power resources to user space
  ACPI / PM: Fix potential problem in acpi_device_get_power()
  • Loading branch information
Rafael J. Wysocki committed Apr 27, 2013
2 parents 0ad4991 + 41a2a46 commit 34bdb1a
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 34 deletions.
13 changes: 13 additions & 0 deletions Documentation/ABI/testing/sysfs-devices-power_resources_wakeup
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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.
39 changes: 24 additions & 15 deletions drivers/acpi/device_pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/*
Expand Down
58 changes: 39 additions & 19 deletions drivers/acpi/power.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)
Expand Down

0 comments on commit 34bdb1a

Please sign in to comment.