Commit 18a38709 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

ACPI / PM: Expose lists of device power resources to user space

Since ACPI power resources are going to be used more extensively on
new hardware platforms, it is necessary to allow user space (powertop
in particular) to look at the lists of power resources corresponding
to different power states of devices for diagnostics and control
purposes.

For this reason, for each power state of an ACPI device node using
power resources create a special attribute group under the device
node's directory in sysfs containing links to sysfs directories
representing the power resources in that list.  The names of the
new attribute groups are "power_resources_<state>", where <state>
is the state name i.e. "D0", "D1", "D2", or "D3hot".
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0bb8f3d6
What: /sys/devices/.../power_resources_D0/
Date: January 2013
Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
The /sys/devices/.../power_resources_D0/ directory is only
present for device objects representing ACPI device nodes that
use ACPI power resources for power management.
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 in ACPI power state D0. The names
of the links are the same as the names of the directories they
point to.
What: /sys/devices/.../power_resources_D1/
Date: January 2013
Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
The /sys/devices/.../power_resources_D1/ directory is only
present for device objects representing ACPI device nodes that
use ACPI power resources for power management and support ACPI
power state D1.
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 in ACPI power state D1. The names
of the links are the same as the names of the directories they
point to.
What: /sys/devices/.../power_resources_D2/
Date: January 2013
Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
The /sys/devices/.../power_resources_D2/ directory is only
present for device objects representing ACPI device nodes that
use ACPI power resources for power management and support ACPI
power state D2.
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 in ACPI power state D2. The names
of the links are the same as the names of the directories they
point to.
What: /sys/devices/.../power_resources_D3hot/
Date: January 2013
Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
The /sys/devices/.../power_resources_D3hot/ directory is only
present for device objects representing ACPI device nodes that
use ACPI power resources for power management and support ACPI
power state D3hot.
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 in ACPI power state D3hot. The
names of the links are the same as the names of the directories
they point to.
......@@ -41,6 +41,7 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/sysfs.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include "sleep.h"
......@@ -417,24 +418,101 @@ static void acpi_power_remove_dependent(struct acpi_power_resource *resource,
}
}
void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
static struct attribute *attrs[] = {
NULL,
};
static struct attribute_group attr_groups[] = {
[ACPI_STATE_D0] = {
.name = "power_resources_D0",
.attrs = attrs,
},
[ACPI_STATE_D1] = {
.name = "power_resources_D1",
.attrs = attrs,
},
[ACPI_STATE_D2] = {
.name = "power_resources_D2",
.attrs = attrs,
},
[ACPI_STATE_D3_HOT] = {
.name = "power_resources_D3hot",
.attrs = attrs,
},
};
static void acpi_power_hide_list(struct acpi_device *adev, int state)
{
struct acpi_device_power_state *ps = &adev->power.states[state];
struct acpi_power_resource_entry *entry;
if (list_empty(&ps->resources))
return;
list_for_each_entry_reverse(entry, &ps->resources, node) {
struct acpi_device *res_dev = &entry->resource->device;
sysfs_remove_link_from_group(&adev->dev.kobj,
attr_groups[state].name,
dev_name(&res_dev->dev));
}
sysfs_remove_group(&adev->dev.kobj, &attr_groups[state]);
}
static void acpi_power_expose_list(struct acpi_device *adev, int state)
{
if (adev->power.flags.power_resources) {
struct acpi_device_power_state *ps;
struct acpi_power_resource_entry *entry;
ps = &adev->power.states[ACPI_STATE_D0];
list_for_each_entry(entry, &ps->resources, node) {
struct acpi_power_resource *resource = entry->resource;
if (add)
acpi_power_add_dependent(resource, adev);
else
acpi_power_remove_dependent(resource, adev);
struct acpi_device_power_state *ps = &adev->power.states[state];
struct acpi_power_resource_entry *entry;
int ret;
if (list_empty(&ps->resources))
return;
ret = sysfs_create_group(&adev->dev.kobj, &attr_groups[state]);
if (ret)
return;
list_for_each_entry(entry, &ps->resources, node) {
struct acpi_device *res_dev = &entry->resource->device;
ret = sysfs_add_link_to_group(&adev->dev.kobj,
attr_groups[state].name,
&res_dev->dev.kobj,
dev_name(&res_dev->dev));
if (ret) {
acpi_power_hide_list(adev, state);
break;
}
}
}
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->power.flags.power_resources)
return;
ps = &adev->power.states[ACPI_STATE_D0];
list_for_each_entry(entry, &ps->resources, node) {
struct acpi_power_resource *resource = entry->resource;
if (add)
acpi_power_add_dependent(resource, adev);
else
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);
}
}
int acpi_power_min_system_level(struct list_head *list)
{
struct acpi_power_resource_entry *entry;
......
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