summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-01-25 21:51:32 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-01-25 21:51:32 +0100
commit18a387099b3e3fd901cc706f708b163aa45347b6 (patch)
treecda9262fcb78ab88fcab0530c6cb4128fe7910ce /drivers/acpi/power.c
parent0bb8f3d6ae621945e6fa2102aa894f72b76a023e (diff)
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: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r--drivers/acpi/power.c104
1 files changed, 91 insertions, 13 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 946720a4db5..9466f56b938 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -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;