From bc9b6407bd6df3ab7189e5622816bbc11ae9d2d8 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 17 Jan 2013 14:11:05 +0100 Subject: ACPI / PM: Rework the handling of devices depending on power resources Commit 0090def6 (ACPI: Add interface to register/unregister device to/from power resources) made it possible to indicate to the ACPI core that if the given device depends on any power resources, then it should be resumed as soon as all of the power resources required by it to transition to the D0 power state have been turned on. Unfortunately, however, this was a mistake, because all devices depending on power resources should be treated this way (i.e. they should be resumed when all power resources required by their D0 state have been turned on) and for the majority of those devices the ACPI core can figure out by itself which (physical) devices depend on what power resources. For this reason, replace the code added by commit 0090def6 with a new, much more straightforward, mechanism that will be used internally by the ACPI core and remove all references to that code from kernel subsystems using ACPI. For the cases when there are (physical) devices that should be resumed whenever a not directly related ACPI device node goes into D0 as a result of power resources configuration changes, like in the SATA case, add two new routines, acpi_dev_pm_add_dependent() and acpi_dev_pm_remove_dependent(), allowing subsystems to manage such dependencies. Convert the SATA subsystem to use the new functions accordingly. Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 567851b4f043..29a1badfca55 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -279,6 +279,7 @@ struct acpi_device { struct list_head physical_node_list; struct mutex physical_node_lock; DECLARE_BITMAP(physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); + struct list_head power_dependent; }; static inline void *acpi_driver_data(struct acpi_device *d) @@ -334,8 +335,6 @@ int acpi_device_set_power(struct acpi_device *device, int state); int acpi_bus_update_power(acpi_handle handle, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); bool acpi_bus_can_wakeup(acpi_handle handle); -int acpi_power_resource_register_device(struct device *dev, acpi_handle handle); -void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle); #ifdef CONFIG_ACPI_PROC_EVENT int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); @@ -414,6 +413,8 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, int acpi_device_power_state(struct device *dev, struct acpi_device *adev, u32 target_state, int d_max_in, int *d_min_p); int acpi_pm_device_sleep_state(struct device *, int *, int); +void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev); +void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev); #else static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, acpi_notify_handler handler, @@ -443,6 +444,10 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) { return __acpi_device_power_state(m, p); } +static inline void acpi_dev_pm_add_dependent(acpi_handle handle, + struct device *depdev) {} +static inline void acpi_dev_pm_remove_dependent(acpi_handle handle, + struct device *depdev) {} #endif #ifdef CONFIG_PM_RUNTIME -- cgit v1.2.3-55-g7522 From 0b224527323669c66e0a37ae05b04034bfcdce14 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 17 Jan 2013 14:11:06 +0100 Subject: ACPI / PM: Take order attribute of power resources into account ACPI power resources have an order attribute that should be taken into account when turning them on and off, but it is not used now. Modify the power resources management code to preserve the spec-compliant ordering of power resources that power states of devices depend on (analogous changes will be done separately for power resources used for wakeup). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 2 + drivers/acpi/power.c | 161 ++++++++++++++++++++++++++---------------------- drivers/acpi/scan.c | 31 ++++++++-- include/acpi/acpi_bus.h | 2 +- 4 files changed, 118 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ce6cb24de8c7..e28068a765a9 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -50,6 +50,8 @@ void acpi_free_ids(struct acpi_device *device); Power Resource -------------------------------------------------------------------------- */ int acpi_power_init(void); +void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list); +void acpi_power_resources_list_free(struct list_head *list); void acpi_add_power_resource(acpi_handle handle); void acpi_power_add_remove_device(struct acpi_device *adev, bool add); int acpi_device_sleep_wake(struct acpi_device *dev, diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 06ad05288af8..22a3d00d0359 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -75,6 +75,11 @@ struct acpi_power_resource { struct mutex resource_lock; }; +struct acpi_power_resource_entry { + struct list_head node; + struct acpi_power_resource *resource; +}; + static LIST_HEAD(acpi_power_resource_list); static DEFINE_MUTEX(power_resource_list_lock); @@ -92,6 +97,41 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) return container_of(device, struct acpi_power_resource, device); } +void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list) +{ + struct acpi_power_resource *resource = acpi_power_get_context(handle); + struct acpi_power_resource_entry *entry; + + if (!resource || !list) + return; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + entry->resource = resource; + if (!list_empty(list)) { + struct acpi_power_resource_entry *e; + + list_for_each_entry(e, list, node) + if (e->resource->order > resource->order) { + list_add_tail(&entry->node, &e->node); + return; + } + } + list_add_tail(&entry->node, list); +} + +void acpi_power_resources_list_free(struct list_head *list) +{ + struct acpi_power_resource_entry *entry, *e; + + list_for_each_entry_safe(entry, e, list, node) { + list_del(&entry->node); + kfree(entry); + } +} + static int acpi_power_get_state(acpi_handle handle, int *state) { acpi_status status = AE_OK; @@ -119,31 +159,23 @@ static int acpi_power_get_state(acpi_handle handle, int *state) return 0; } -static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) +static int acpi_power_get_list_state(struct list_head *list, int *state) { + struct acpi_power_resource_entry *entry; int cur_state; - int i = 0; if (!list || !state) return -EINVAL; /* The state of the list is 'on' IFF all resources are 'on'. */ - - for (i = 0; i < list->count; i++) { - struct acpi_power_resource *resource; - acpi_handle handle = list->handles[i]; + list_for_each_entry(entry, list, node) { + struct acpi_power_resource *resource = entry->resource; + acpi_handle handle = resource->device.handle; int result; - resource = acpi_power_get_context(handle); - if (!resource) - return -ENODEV; - mutex_lock(&resource->resource_lock); - result = acpi_power_get_state(handle, &cur_state); - mutex_unlock(&resource->resource_lock); - if (result) return result; @@ -155,7 +187,6 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) cur_state ? "on" : "off")); *state = cur_state; - return 0; } @@ -199,14 +230,9 @@ static int __acpi_power_on(struct acpi_power_resource *resource) return 0; } -static int acpi_power_on(acpi_handle handle) +static int acpi_power_on(struct acpi_power_resource *resource) { - int result = 0; - struct acpi_power_resource *resource; - - resource = acpi_power_get_context(handle); - if (!resource) - return -ENODEV; + int result = 0;; mutex_lock(&resource->resource_lock); @@ -231,15 +257,10 @@ static int acpi_power_on(acpi_handle handle) return result; } -static int acpi_power_off(acpi_handle handle) +static int acpi_power_off(struct acpi_power_resource *resource) { - int result = 0; acpi_status status = AE_OK; - struct acpi_power_resource *resource; - - resource = acpi_power_get_context(handle); - if (!resource) - return -ENODEV; + int result = 0; mutex_lock(&resource->resource_lock); @@ -271,47 +292,48 @@ static int acpi_power_off(acpi_handle handle) return result; } -static void __acpi_power_off_list(struct acpi_handle_list *list, int num_res) +static int acpi_power_off_list(struct list_head *list) { - int i; + struct acpi_power_resource_entry *entry; + int result = 0; - for (i = num_res - 1; i >= 0 ; i--) - acpi_power_off(list->handles[i]); -} + list_for_each_entry_reverse(entry, list, node) { + result = acpi_power_off(entry->resource); + if (result) + goto err; + } + return 0; -static void acpi_power_off_list(struct acpi_handle_list *list) -{ - __acpi_power_off_list(list, list->count); + err: + list_for_each_entry_continue(entry, list, node) + acpi_power_on(entry->resource); + + return result; } -static int acpi_power_on_list(struct acpi_handle_list *list) +static int acpi_power_on_list(struct list_head *list) { + struct acpi_power_resource_entry *entry; int result = 0; - int i; - for (i = 0; i < list->count; i++) { - result = acpi_power_on(list->handles[i]); - if (result) { - __acpi_power_off_list(list, i); - break; - } + list_for_each_entry(entry, list, node) { + result = acpi_power_on(entry->resource); + if (result) + goto err; } + return 0; + + err: + list_for_each_entry_continue_reverse(entry, list, node) + acpi_power_off(entry->resource); return result; } -static void acpi_power_add_dependent(acpi_handle rhandle, +static void acpi_power_add_dependent(struct acpi_power_resource *resource, struct acpi_device *adev) { struct acpi_power_dependent_device *dep; - struct acpi_power_resource *resource; - - if (!rhandle || !adev) - return; - - resource = acpi_power_get_context(rhandle); - if (!resource) - return; mutex_lock(&resource->resource_lock); @@ -331,20 +353,12 @@ static void acpi_power_add_dependent(acpi_handle rhandle, mutex_unlock(&resource->resource_lock); } -static void acpi_power_remove_dependent(acpi_handle rhandle, +static void acpi_power_remove_dependent(struct acpi_power_resource *resource, struct acpi_device *adev) { struct acpi_power_dependent_device *dep; - struct acpi_power_resource *resource; struct work_struct *work = NULL; - if (!rhandle || !adev) - return; - - resource = acpi_power_get_context(rhandle); - if (!resource) - return; - mutex_lock(&resource->resource_lock); list_for_each_entry(dep, &resource->dependent, node) @@ -366,16 +380,16 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add) { if (adev->power.flags.power_resources) { struct acpi_device_power_state *ps; - int j; + struct acpi_power_resource_entry *entry; ps = &adev->power.states[ACPI_STATE_D0]; - for (j = 0; j < ps->resources.count; j++) { - acpi_handle rhandle = ps->resources.handles[j]; + list_for_each_entry(entry, &ps->resources, node) { + struct acpi_power_resource *resource = entry->resource; if (add) - acpi_power_add_dependent(rhandle, adev); + acpi_power_add_dependent(resource, adev); else - acpi_power_remove_dependent(rhandle, adev); + acpi_power_remove_dependent(resource, adev); } } } @@ -539,7 +553,6 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) int acpi_power_get_inferred_state(struct acpi_device *device, int *state) { int result = 0; - struct acpi_handle_list *list = NULL; int list_state = 0; int i = 0; @@ -551,8 +564,9 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) * required for a given D-state are 'on'. */ for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { - list = &device->power.states[i].resources; - if (list->count < 1) + struct list_head *list = &device->power.states[i].resources; + + if (list_empty(list)) continue; result = acpi_power_get_list_state(list, &list_state); @@ -571,9 +585,12 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) int acpi_power_on_resources(struct acpi_device *device, int state) { - if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3) + if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_COLD) return -EINVAL; + if (state == ACPI_STATE_D3_COLD) + return 0; + return acpi_power_on_list(&device->power.states[state].resources); } @@ -584,7 +601,7 @@ int acpi_power_transition(struct acpi_device *device, int state) if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) return -EINVAL; - if (device->power.state == state) + if (device->power.state == state || !device->flags.power_manageable) return 0; if ((device->power.state < ACPI_STATE_D0) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c7ea9c2649a4..d557868c0081 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -475,11 +475,25 @@ void acpi_free_ids(struct acpi_device *device) kfree(device->pnp.unique_id); } +static void acpi_free_power_resources_lists(struct acpi_device *device) +{ + int i; + + if (!device->flags.power_manageable) + return; + + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { + struct acpi_device_power_state *ps = &device->power.states[i]; + acpi_power_resources_list_free(&ps->resources); + } +} + static void acpi_device_release(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); acpi_free_ids(acpi_dev); + acpi_free_power_resources_lists(acpi_dev); kfree(acpi_dev); } @@ -1055,17 +1069,22 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { struct acpi_device_power_state *ps = &device->power.states[i]; char object_name[5] = { '_', 'P', 'R', '0' + i, '\0' }; + struct acpi_handle_list resources; + INIT_LIST_HEAD(&ps->resources); /* Evaluate "_PRx" to se if power resources are referenced */ acpi_evaluate_reference(device->handle, object_name, NULL, - &ps->resources); - if (ps->resources.count) { + &resources); + if (resources.count) { int j; device->power.flags.power_resources = 1; - for (j = 0; j < ps->resources.count; j++) { - acpi_handle rhandle = ps->resources.handles[j]; + for (j = 0; j < resources.count; j++) { + acpi_handle rhandle = resources.handles[j]; + acpi_add_power_resource(rhandle); + acpi_power_resources_list_add(rhandle, + &ps->resources); } } @@ -1079,7 +1098,7 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) * State is valid if there are means to put the device into it. * D3hot is only valid if _PR3 present. */ - if (ps->resources.count || + if (resources.count || (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) { ps->flags.valid = 1; ps->flags.os_accessible = 1; @@ -1089,6 +1108,8 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) ps->latency = -1; /* Unknown - driver assigned */ } + INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources); + /* Set defaults for D0 and D3 states (always valid) */ device->power.states[ACPI_STATE_D0].flags.valid = 1; device->power.states[ACPI_STATE_D0].power = 100; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 29a1badfca55..32dc679d2c71 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -199,7 +199,7 @@ struct acpi_device_power_state { } flags; int power; /* % Power (compared to D0) */ int latency; /* Dx->D0 time (microseconds) */ - struct acpi_handle_list resources; /* Power resources referenced */ + struct list_head resources; /* Power resources referenced */ }; struct acpi_device_power { -- cgit v1.2.3-55-g7522 From 993cbe595dda731471a07f4f65575fadedc570dc Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 17 Jan 2013 14:11:06 +0100 Subject: ACPI / PM: Take order attribute of wakeup power resources into account ACPI power resources have an order attribute that should be taken into account when turning them on and off, but it is not used now. Modify the power resources management code to preserve the spec-compliant ordering of wakeup power resources. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 44 ++++++++++++++++---------------------------- drivers/acpi/scan.c | 26 ++++++++++++++++---------- include/acpi/acpi_bus.h | 2 +- 3 files changed, 33 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 22a3d00d0359..242feca231eb 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -469,7 +469,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev, */ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) { - int i, err = 0; + int err = 0; if (!dev || !dev->wakeup.flags.valid) return -EINVAL; @@ -479,24 +479,17 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) if (dev->wakeup.prepare_count++) goto out; - /* Open power resource */ - for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_on(dev->wakeup.resources.handles[i]); - if (ret) { - printk(KERN_ERR PREFIX "Transition power state\n"); - dev->wakeup.flags.valid = 0; - err = -ENODEV; - goto err_out; - } + err = acpi_power_on_list(&dev->wakeup.resources); + if (err) { + dev_err(&dev->dev, "Cannot turn wakeup power resources on\n"); + dev->wakeup.flags.valid = 0; + } else { + /* + * Passing 3 as the third argument below means the device may be + * put into arbitrary power state afterward. + */ + err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); } - - /* - * Passing 3 as the third argument below means the device may be placed - * in arbitrary power state afterwards. - */ - err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); - - err_out: if (err) dev->wakeup.prepare_count = 0; @@ -513,7 +506,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) */ int acpi_disable_wakeup_device_power(struct acpi_device *dev) { - int i, err = 0; + int err = 0; if (!dev || !dev->wakeup.flags.valid) return -EINVAL; @@ -534,15 +527,10 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) if (err) goto out; - /* Close power resource */ - for (i = 0; i < dev->wakeup.resources.count; i++) { - int ret = acpi_power_off(dev->wakeup.resources.handles[i]); - if (ret) { - printk(KERN_ERR PREFIX "Transition power state\n"); - dev->wakeup.flags.valid = 0; - err = -ENODEV; - goto out; - } + err = acpi_power_off_list(&dev->wakeup.resources); + if (err) { + dev_err(&dev->dev, "Cannot turn wakeup power resources off\n"); + dev->wakeup.flags.valid = 0; } out: diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d557868c0081..e4ac46a9c664 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -479,6 +479,9 @@ static void acpi_free_power_resources_lists(struct acpi_device *device) { int i; + if (device->wakeup.flags.valid) + acpi_power_resources_list_free(&device->wakeup.resources); + if (!device->flags.power_manageable) return; @@ -902,6 +905,8 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, if (!wakeup) return AE_BAD_PARAMETER; + INIT_LIST_HEAD(&wakeup->resources); + /* _PRW */ status = acpi_evaluate_object(handle, "_PRW", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -948,19 +953,17 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, } wakeup->sleep_state = element->integer.value; - if ((package->package.count - 2) > ACPI_MAX_HANDLES) { - status = AE_NO_MEMORY; - goto out; - } - wakeup->resources.count = package->package.count - 2; - for (i = 0; i < wakeup->resources.count; i++) { - element = &(package->package.elements[i + 2]); + for (i = 2; i < package->package.count; i++) { + acpi_handle rhandle; + + element = &(package->package.elements[i]); if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { status = AE_BAD_DATA; goto out; } - - wakeup->resources.handles[i] = element->reference.handle; + rhandle = element->reference.handle; + acpi_add_power_resource(rhandle); + acpi_power_resources_list_add(rhandle, &wakeup->resources); } acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); @@ -1018,6 +1021,7 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) status = acpi_bus_extract_wakeup_device_power_package(device->handle, &device->wakeup); if (ACPI_FAILURE(status)) { + acpi_power_resources_list_free(&device->wakeup.resources); ACPI_EXCEPTION((AE_INFO, status, "Extracting _PRW package")); return; } @@ -1491,9 +1495,11 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, acpi_handle temp; status = acpi_get_handle(handle, "_PRW", &temp); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { acpi_bus_extract_wakeup_device_power_package(handle, &wakeup); + acpi_power_resources_list_free(&wakeup.resources); + } return AE_CTRL_DEPTH; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 32dc679d2c71..a272c3156999 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -242,7 +242,7 @@ struct acpi_device_wakeup { acpi_handle gpe_device; u64 gpe_number; u64 sleep_state; - struct acpi_handle_list resources; + struct list_head resources; struct acpi_device_wakeup_flags flags; int prepare_count; }; -- cgit v1.2.3-55-g7522 From ad0c3b0e4863185a9f8874a655a8d2999c915131 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 17 Jan 2013 14:11:08 +0100 Subject: ACPI / PM: More visible function for retrieving device power states The function used for retrieving ACPI device power states, __acpi_bus_get_power(), is now static, because it is only used internally in drivers/acpi/bus.c. However, it will be used outside of that file going forward, so rename it to acpi_device_get_power(), in analogy with acpi_device_set_power(), add a kerneldoc comment to it and add its header to acpi_bus.h. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 15 ++++++++++++--- include/acpi/acpi_bus.h | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 8e57fc49726e..0e1441cc4d7f 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -200,7 +200,16 @@ static const char *state_string(int state) } } -static int __acpi_bus_get_power(struct acpi_device *device, int *state) +/** + * acpi_device_get_power - Get power state of an ACPI device. + * @device: Device to get the power state of. + * @state: Place to store the power state of the device. + * + * This function does not update the device's power.state field, but it may + * update its parent's power.state field (when the parent's power state is + * unknown and the device's power state turns out to be D0). + */ +int acpi_device_get_power(struct acpi_device *device, int *state) { int result = ACPI_STATE_UNKNOWN; @@ -397,7 +406,7 @@ int acpi_bus_init_power(struct acpi_device *device) device->power.state = ACPI_STATE_UNKNOWN; - result = __acpi_bus_get_power(device, &state); + result = acpi_device_get_power(device, &state); if (result) return result; @@ -421,7 +430,7 @@ int acpi_bus_update_power(acpi_handle handle, int *state_p) if (result) return result; - result = __acpi_bus_get_power(device, &state); + result = acpi_device_get_power(device, &state); if (result) return result; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a272c3156999..9d7c2ca0f1f7 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -331,6 +331,7 @@ acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta); int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_set_power(acpi_handle handle, int state); +int acpi_device_get_power(struct acpi_device *device, int *state); int acpi_device_set_power(struct acpi_device *device, int state); int acpi_bus_update_power(acpi_handle handle, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); -- cgit v1.2.3-55-g7522 From 96bfd3cee2a741906b3ef5c1096d2f0a0b8025e0 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 17 Jan 2013 14:11:08 +0100 Subject: ACPI / PM: Common string representations of device power states The function returning string representations of ACPI device power states, state_string((), is now static, because it is only used internally in drivers/acpi/bus.c. However, it will be used outside of that file going forward, so rename it to acpi_power_state_string(), add a kerneldoc comment to it and add its header to acpi_bus.h. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 18 ++++++++++++------ include/acpi/acpi_bus.h | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 0e1441cc4d7f..6c9b16c16660 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -182,7 +182,11 @@ EXPORT_SYMBOL(acpi_bus_get_private_data); Power Management -------------------------------------------------------------------------- */ -static const char *state_string(int state) +/** + * acpi_power_state_string - String representation of ACPI device power state. + * @state: ACPI device power state to return the string representation of. + */ +const char *acpi_power_state_string(int state) { switch (state) { case ACPI_STATE_D0: @@ -260,7 +264,7 @@ int acpi_device_get_power(struct acpi_device *device, int *state) out: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", - device->pnp.bus_id, state_string(*state))); + device->pnp.bus_id, acpi_power_state_string(*state))); return 0; } @@ -288,13 +292,13 @@ int acpi_device_set_power(struct acpi_device *device, int state) if (state == device->power.state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n", - state_string(state))); + acpi_power_state_string(state))); return 0; } if (!device->power.states[state].flags.valid) { printk(KERN_WARNING PREFIX "Device does not support %s\n", - state_string(state)); + acpi_power_state_string(state)); return -ENODEV; } if (device->parent && (state < device->parent->power.state)) { @@ -362,12 +366,14 @@ int acpi_device_set_power(struct acpi_device *device, int state) if (result) printk(KERN_WARNING PREFIX "Device [%s] failed to transition to %s\n", - device->pnp.bus_id, state_string(state)); + device->pnp.bus_id, + acpi_power_state_string(state)); else { device->power.state = state; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] transitioned to %s\n", - device->pnp.bus_id, state_string(state))); + device->pnp.bus_id, + acpi_power_state_string(state))); } return result; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9d7c2ca0f1f7..71eceb99d411 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -331,6 +331,7 @@ acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta); int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_set_power(acpi_handle handle, int state); +const char *acpi_power_state_string(int state); int acpi_device_get_power(struct acpi_device *device, int *state); int acpi_device_set_power(struct acpi_device *device, int state); int acpi_bus_update_power(acpi_handle handle, int *state_p); -- cgit v1.2.3-55-g7522 From 9ce4e607111764673f7a59d7bc87a16ade5c7bba Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 17 Jan 2013 14:11:08 +0100 Subject: ACPI / PM: Move device power management functions to device_pm.c Move ACPI device power management functions from drivers/acpi/bus.c to drivers/acpi/device_pm.c. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 293 ----------------------------------------------- drivers/acpi/device_pm.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/internal.h | 1 - include/acpi/acpi_bus.h | 38 ++++++ 4 files changed, 326 insertions(+), 294 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 6c9b16c16660..01708a165368 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -178,299 +178,6 @@ int acpi_bus_get_private_data(acpi_handle handle, void **data) } EXPORT_SYMBOL(acpi_bus_get_private_data); -/* -------------------------------------------------------------------------- - Power Management - -------------------------------------------------------------------------- */ - -/** - * acpi_power_state_string - String representation of ACPI device power state. - * @state: ACPI device power state to return the string representation of. - */ -const char *acpi_power_state_string(int state) -{ - switch (state) { - case ACPI_STATE_D0: - return "D0"; - case ACPI_STATE_D1: - return "D1"; - case ACPI_STATE_D2: - return "D2"; - case ACPI_STATE_D3_HOT: - return "D3hot"; - case ACPI_STATE_D3_COLD: - return "D3"; - default: - return "(unknown)"; - } -} - -/** - * acpi_device_get_power - Get power state of an ACPI device. - * @device: Device to get the power state of. - * @state: Place to store the power state of the device. - * - * This function does not update the device's power.state field, but it may - * update its parent's power.state field (when the parent's power state is - * unknown and the device's power state turns out to be D0). - */ -int acpi_device_get_power(struct acpi_device *device, int *state) -{ - int result = ACPI_STATE_UNKNOWN; - - if (!device || !state) - return -EINVAL; - - if (!device->flags.power_manageable) { - /* TBD: Non-recursive algorithm for walking up hierarchy. */ - *state = device->parent ? - device->parent->power.state : ACPI_STATE_D0; - goto out; - } - - /* - * Get the device's power state either directly (via _PSC) or - * indirectly (via power resources). - */ - 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) { - 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 we were unsure about the device parent's power state up to this - * point, the fact that the device is in D0 implies that the parent has - * to be in D0 too. - */ - if (device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN - && result == ACPI_STATE_D0) - device->parent->power.state = ACPI_STATE_D0; - - *state = result; - - out: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", - device->pnp.bus_id, acpi_power_state_string(*state))); - - return 0; -} - - -/** - * acpi_device_set_power - Set power state of an ACPI device. - * @device: Device to set the power state of. - * @state: New power state to set. - * - * Callers must ensure that the device is power manageable before using this - * function. - */ -int acpi_device_set_power(struct acpi_device *device, int state) -{ - int result = 0; - acpi_status status = AE_OK; - char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; - bool cut_power = false; - - if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) - return -EINVAL; - - /* Make sure this is a valid target state */ - - if (state == device->power.state) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n", - acpi_power_state_string(state))); - return 0; - } - - if (!device->power.states[state].flags.valid) { - printk(KERN_WARNING PREFIX "Device does not support %s\n", - acpi_power_state_string(state)); - return -ENODEV; - } - if (device->parent && (state < device->parent->power.state)) { - printk(KERN_WARNING PREFIX - "Cannot set device to a higher-powered" - " state than parent\n"); - return -ENODEV; - } - - /* For D3cold we should first transition into D3hot. */ - if (state == ACPI_STATE_D3_COLD - && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { - state = ACPI_STATE_D3_HOT; - object_name[3] = '3'; - cut_power = true; - } - - /* - * Transition Power - * ---------------- - * On transitions to a high-powered state we first apply power (via - * power resources) then evalute _PSx. Conversly for transitions to - * a lower-powered state. - */ - if (state < device->power.state) { - if (device->power.state >= ACPI_STATE_D3_HOT && - state != ACPI_STATE_D0) { - printk(KERN_WARNING PREFIX - "Cannot transition to non-D0 state from D3\n"); - return -ENODEV; - } - if (device->power.flags.power_resources) { - result = acpi_power_transition(device, state); - if (result) - goto end; - } - if (device->power.states[state].flags.explicit_set) { - status = acpi_evaluate_object(device->handle, - object_name, NULL, NULL); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } - } - } else { - if (device->power.states[state].flags.explicit_set) { - status = acpi_evaluate_object(device->handle, - object_name, NULL, NULL); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } - } - if (device->power.flags.power_resources) { - result = acpi_power_transition(device, state); - if (result) - goto end; - } - } - - if (cut_power) - result = acpi_power_transition(device, ACPI_STATE_D3_COLD); - - end: - if (result) - printk(KERN_WARNING PREFIX - "Device [%s] failed to transition to %s\n", - device->pnp.bus_id, - acpi_power_state_string(state)); - else { - device->power.state = state; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device [%s] transitioned to %s\n", - device->pnp.bus_id, - acpi_power_state_string(state))); - } - - return result; -} -EXPORT_SYMBOL(acpi_device_set_power); - - -int acpi_bus_set_power(acpi_handle handle, int state) -{ - struct acpi_device *device; - int result; - - result = acpi_bus_get_device(handle, &device); - if (result) - return result; - - if (!device->flags.power_manageable) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device [%s] is not power manageable\n", - dev_name(&device->dev))); - return -ENODEV; - } - - return acpi_device_set_power(device, state); -} -EXPORT_SYMBOL(acpi_bus_set_power); - - -int acpi_bus_init_power(struct acpi_device *device) -{ - int state; - int result; - - if (!device) - return -EINVAL; - - device->power.state = ACPI_STATE_UNKNOWN; - - result = acpi_device_get_power(device, &state); - if (result) - return result; - - if (device->power.flags.power_resources) - result = acpi_power_on_resources(device, state); - - if (!result) - device->power.state = state; - - return result; -} - - -int acpi_bus_update_power(acpi_handle handle, int *state_p) -{ - struct acpi_device *device; - int state; - int result; - - result = acpi_bus_get_device(handle, &device); - if (result) - return result; - - result = acpi_device_get_power(device, &state); - if (result) - return result; - - result = acpi_device_set_power(device, state); - if (!result && state_p) - *state_p = state; - - return result; -} -EXPORT_SYMBOL_GPL(acpi_bus_update_power); - - -bool acpi_bus_power_manageable(acpi_handle handle) -{ - struct acpi_device *device; - int result; - - result = acpi_bus_get_device(handle, &device); - return result ? false : device->flags.power_manageable; -} - -EXPORT_SYMBOL(acpi_bus_power_manageable); - -bool acpi_bus_can_wakeup(acpi_handle handle) -{ - struct acpi_device *device; - int result; - - result = acpi_bus_get_device(handle, &device); - return result ? false : device->wakeup.flags.valid; -} - -EXPORT_SYMBOL(acpi_bus_can_wakeup); - static void acpi_print_osc_error(acpi_handle handle, struct acpi_osc_context *context, char *error) { diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 8be4b29e38aa..8bca7465c78d 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -30,6 +30,12 @@ #include #include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_POWER_COMPONENT +ACPI_MODULE_NAME("device_pm"); static DEFINE_MUTEX(acpi_pm_notifier_lock); @@ -93,6 +99,288 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, return status; } +/** + * acpi_power_state_string - String representation of ACPI device power state. + * @state: ACPI device power state to return the string representation of. + */ +const char *acpi_power_state_string(int state) +{ + switch (state) { + case ACPI_STATE_D0: + return "D0"; + case ACPI_STATE_D1: + return "D1"; + case ACPI_STATE_D2: + return "D2"; + case ACPI_STATE_D3_HOT: + return "D3hot"; + case ACPI_STATE_D3_COLD: + return "D3"; + default: + return "(unknown)"; + } +} + +/** + * acpi_device_get_power - Get power state of an ACPI device. + * @device: Device to get the power state of. + * @state: Place to store the power state of the device. + * + * This function does not update the device's power.state field, but it may + * update its parent's power.state field (when the parent's power state is + * unknown and the device's power state turns out to be D0). + */ +int acpi_device_get_power(struct acpi_device *device, int *state) +{ + int result = ACPI_STATE_UNKNOWN; + + if (!device || !state) + return -EINVAL; + + if (!device->flags.power_manageable) { + /* TBD: Non-recursive algorithm for walking up hierarchy. */ + *state = device->parent ? + device->parent->power.state : ACPI_STATE_D0; + goto out; + } + + /* + * Get the device's power state either directly (via _PSC) or + * indirectly (via power resources). + */ + 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) { + 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 we were unsure about the device parent's power state up to this + * point, the fact that the device is in D0 implies that the parent has + * to be in D0 too. + */ + if (device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN + && result == ACPI_STATE_D0) + device->parent->power.state = ACPI_STATE_D0; + + *state = result; + + out: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", + device->pnp.bus_id, acpi_power_state_string(*state))); + + return 0; +} + +/** + * acpi_device_set_power - Set power state of an ACPI device. + * @device: Device to set the power state of. + * @state: New power state to set. + * + * Callers must ensure that the device is power manageable before using this + * function. + */ +int acpi_device_set_power(struct acpi_device *device, int state) +{ + int result = 0; + acpi_status status = AE_OK; + char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; + bool cut_power = false; + + if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) + return -EINVAL; + + /* Make sure this is a valid target state */ + + if (state == device->power.state) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n", + acpi_power_state_string(state))); + return 0; + } + + if (!device->power.states[state].flags.valid) { + printk(KERN_WARNING PREFIX "Device does not support %s\n", + acpi_power_state_string(state)); + return -ENODEV; + } + if (device->parent && (state < device->parent->power.state)) { + printk(KERN_WARNING PREFIX + "Cannot set device to a higher-powered" + " state than parent\n"); + return -ENODEV; + } + + /* For D3cold we should first transition into D3hot. */ + if (state == ACPI_STATE_D3_COLD + && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { + state = ACPI_STATE_D3_HOT; + object_name[3] = '3'; + cut_power = true; + } + + /* + * Transition Power + * ---------------- + * On transitions to a high-powered state we first apply power (via + * power resources) then evalute _PSx. Conversly for transitions to + * a lower-powered state. + */ + if (state < device->power.state) { + if (device->power.state >= ACPI_STATE_D3_HOT && + state != ACPI_STATE_D0) { + printk(KERN_WARNING PREFIX + "Cannot transition to non-D0 state from D3\n"); + return -ENODEV; + } + if (device->power.flags.power_resources) { + result = acpi_power_transition(device, state); + if (result) + goto end; + } + if (device->power.states[state].flags.explicit_set) { + status = acpi_evaluate_object(device->handle, + object_name, NULL, NULL); + if (ACPI_FAILURE(status)) { + result = -ENODEV; + goto end; + } + } + } else { + if (device->power.states[state].flags.explicit_set) { + status = acpi_evaluate_object(device->handle, + object_name, NULL, NULL); + if (ACPI_FAILURE(status)) { + result = -ENODEV; + goto end; + } + } + if (device->power.flags.power_resources) { + result = acpi_power_transition(device, state); + if (result) + goto end; + } + } + + if (cut_power) + result = acpi_power_transition(device, ACPI_STATE_D3_COLD); + + end: + if (result) + printk(KERN_WARNING PREFIX + "Device [%s] failed to transition to %s\n", + device->pnp.bus_id, + acpi_power_state_string(state)); + else { + device->power.state = state; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Device [%s] transitioned to %s\n", + device->pnp.bus_id, + acpi_power_state_string(state))); + } + + return result; +} +EXPORT_SYMBOL(acpi_device_set_power); + +int acpi_bus_set_power(acpi_handle handle, int state) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + if (result) + return result; + + if (!device->flags.power_manageable) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Device [%s] is not power manageable\n", + dev_name(&device->dev))); + return -ENODEV; + } + + return acpi_device_set_power(device, state); +} +EXPORT_SYMBOL(acpi_bus_set_power); + +int acpi_bus_init_power(struct acpi_device *device) +{ + int state; + int result; + + if (!device) + return -EINVAL; + + device->power.state = ACPI_STATE_UNKNOWN; + + result = acpi_device_get_power(device, &state); + if (result) + return result; + + if (device->power.flags.power_resources) + result = acpi_power_on_resources(device, state); + + if (!result) + device->power.state = state; + + return result; +} + +int acpi_bus_update_power(acpi_handle handle, int *state_p) +{ + struct acpi_device *device; + int state; + int result; + + result = acpi_bus_get_device(handle, &device); + if (result) + return result; + + result = acpi_device_get_power(device, &state); + if (result) + return result; + + result = acpi_device_set_power(device, state); + if (!result && state_p) + *state_p = state; + + return result; +} +EXPORT_SYMBOL_GPL(acpi_bus_update_power); + +bool acpi_bus_power_manageable(acpi_handle handle) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + return result ? false : device->flags.power_manageable; +} +EXPORT_SYMBOL(acpi_bus_power_manageable); + +bool acpi_bus_can_wakeup(acpi_handle handle) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + return result ? false : device->wakeup.flags.valid; +} +EXPORT_SYMBOL(acpi_bus_can_wakeup); + /** * acpi_device_power_state - Get preferred power state of ACPI device. * @dev: Device whose preferred target power state to return. diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 07f61dbd8136..1f004f35bc67 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -61,7 +61,6 @@ int acpi_device_sleep_wake(struct acpi_device *dev, int acpi_power_get_inferred_state(struct acpi_device *device, int *state); int acpi_power_on_resources(struct acpi_device *device, int state); int acpi_power_transition(struct acpi_device *device, int state); -int acpi_bus_init_power(struct acpi_device *device); int acpi_wakeup_device_init(void); void acpi_early_processor_set_pdc(void); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 71eceb99d411..fca1b9cb27d9 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -330,13 +330,51 @@ void acpi_bus_data_handler(acpi_handle handle, void *context); acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta); int acpi_bus_get_status(struct acpi_device *device); + +#ifdef CONFIG_PM int acpi_bus_set_power(acpi_handle handle, int state); const char *acpi_power_state_string(int state); int acpi_device_get_power(struct acpi_device *device, int *state); int acpi_device_set_power(struct acpi_device *device, int state); +int acpi_bus_init_power(struct acpi_device *device); int acpi_bus_update_power(acpi_handle handle, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); bool acpi_bus_can_wakeup(acpi_handle handle); +#else /* !CONFIG_PM */ +static inline int acpi_bus_set_power(acpi_handle handle, int state) +{ + return 0; +} +static inline const char *acpi_power_state_string(int state) +{ + return "D0"; +} +static inline int acpi_device_get_power(struct acpi_device *device, int *state) +{ + return 0; +} +static inline int acpi_device_set_power(struct acpi_device *device, int state) +{ + return 0; +} +static inline int acpi_bus_init_power(struct acpi_device *device) +{ + return 0; +} +static inline int acpi_bus_update_power(acpi_handle handle, int *state_p) +{ + return 0; +} +static inline bool acpi_bus_power_manageable(acpi_handle handle) +{ + return false; +} +static inline bool acpi_bus_can_wakeup(acpi_handle handle) +{ + return false; +} +#endif /* !CONFIG_PM */ + #ifdef CONFIG_ACPI_PROC_EVENT int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); -- cgit v1.2.3-55-g7522 From d79beb39922e41083e8bbbb3de084a6ca958e25f Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Sat, 19 Jan 2013 14:29:31 +0100 Subject: ACPI / PM: Fix build for unusual combination of Kconfig options CONFIG_PM_SLEEP may be set even if CONFIG_ACPI_SLEEP is unset, although that is unusual. For this reason, make the headers of functions built for both CONFIG_ACPI and CONFIG_PM_SLEEP set simultaneously depend on that combination of Kconfig options instead of CONFIG_ACPI_SLEEP. This fixes a build problem reported by Randy Dunlap. Signed-off-by: Rafael J. Wysocki --- include/linux/acpi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 8c1d6f2a2193..5f2be833aaf8 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -511,7 +511,7 @@ static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } #endif -#ifdef CONFIG_ACPI_SLEEP +#if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP) int acpi_dev_suspend_late(struct device *dev); int acpi_dev_resume_early(struct device *dev); int acpi_subsys_prepare(struct device *dev); -- cgit v1.2.3-55-g7522 From b1c0f99bfb89cd9b42e3119ab822a8102fa87909 Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Thu, 24 Jan 2013 12:50:09 +0100 Subject: ACPI / PM: Expose current status of ACPI power resources Since ACPI power resources are going to be used more extensively on new hardware platforms, it becomes necessary for user space (powertop in particular) to observe some properties of those resources for diagnostics purposes. For this reason, expose the current status of each ACPI power resource to user space via sysfs by adding a new resource_in_use attribute to the sysfs directory representing the given power resource. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-devices-resource_in_use | 12 ++++++++++ drivers/acpi/power.c | 26 +++++++++++++++++++++- drivers/acpi/scan.c | 3 +++ include/acpi/acpi_bus.h | 1 + 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-resource_in_use (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-devices-resource_in_use b/Documentation/ABI/testing/sysfs-devices-resource_in_use new file mode 100644 index 000000000000..b4a3bc5922a3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-resource_in_use @@ -0,0 +1,12 @@ +What: /sys/devices/.../resource_in_use +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../resource_in_use attribute is only present + for device objects representing ACPI power resources. + + If present, it contains a number (0 or 1) representing the + current status of the given power resource (0 means that the + resource is not in use and therefore it has been turned off). + + This attribute is read-only. diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 3f16dd4db23e..946720a4db57 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -87,6 +87,12 @@ static DEFINE_MUTEX(power_resource_list_lock); Power Resource Management -------------------------------------------------------------------------- */ +static inline +struct acpi_power_resource *to_power_resource(struct acpi_device *device) +{ + return container_of(device, struct acpi_power_resource, device); +} + static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { struct acpi_device *device; @@ -94,7 +100,7 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) if (acpi_bus_get_device(handle, &device)) return NULL; - return container_of(device, struct acpi_power_resource, device); + return to_power_resource(device); } static int acpi_power_resources_list_add(acpi_handle handle, @@ -678,6 +684,21 @@ static void acpi_release_power_resource(struct device *dev) kfree(resource); } +static ssize_t acpi_power_in_use_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct acpi_power_resource *resource; + + resource = to_power_resource(to_acpi_device(dev)); + return sprintf(buf, "%u\n", !!resource->ref_count); +} +static DEVICE_ATTR(resource_in_use, 0444, acpi_power_in_use_show, NULL); + +static void acpi_power_sysfs_remove(struct acpi_device *device) +{ + device_remove_file(&device->dev, &dev_attr_resource_in_use); +} + int acpi_add_power_resource(acpi_handle handle) { struct acpi_power_resource *resource; @@ -725,6 +746,9 @@ int acpi_add_power_resource(acpi_handle handle) if (result) goto err; + if (!device_create_file(&device->dev, &dev_attr_resource_in_use)) + device->remove = acpi_power_sysfs_remove; + mutex_lock(&power_resource_list_lock); list_add(&resource->list_node, &acpi_power_resource_list); mutex_unlock(&power_resource_list_lock); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9761d589f3f5..9801837876b7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -791,6 +791,9 @@ static void acpi_device_unregister(struct acpi_device *device) acpi_power_add_remove_device(device, false); acpi_device_remove_files(device); + if (device->remove) + device->remove(device); + device_del(&device->dev); /* * Drop the reference counts of all power resources the device depends diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index fca1b9cb27d9..aef56a9f4e70 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -280,6 +280,7 @@ struct acpi_device { struct mutex physical_node_lock; DECLARE_BITMAP(physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); struct list_head power_dependent; + void (*remove)(struct acpi_device *); }; static inline void *acpi_driver_data(struct acpi_device *d) -- cgit v1.2.3-55-g7522 From 0bb8f3d6ae621945e6fa2102aa894f72b76a023e Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Fri, 25 Jan 2013 21:51:13 +0100 Subject: sysfs: Functions for adding/removing symlinks to/from attribute groups The most convenient way to expose ACPI power resources lists of a device is to put symbolic links to sysfs directories representing those resources into special attribute groups in the device's sysfs directory. For this purpose, it is necessary to be able to add symbolic links to attribute groups. For this reason, add sysfs helper functions for adding/removing symbolic links to/from attribute groups, sysfs_add_link_to_group() and sysfs_remove_link_from_group(), respectively. This change set includes a build fix from David Rientjes. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- fs/sysfs/group.c | 42 ++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/symlink.c | 45 ++++++++++++++++++++++++++++++++------------- fs/sysfs/sysfs.h | 2 ++ include/linux/sysfs.h | 16 ++++++++++++++++ 4 files changed, 92 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 2df555c66d57..aec3d5c98c94 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -205,6 +205,48 @@ void sysfs_unmerge_group(struct kobject *kobj, } EXPORT_SYMBOL_GPL(sysfs_unmerge_group); +/** + * sysfs_add_link_to_group - add a symlink to an attribute group. + * @kobj: The kobject containing the group. + * @group_name: The name of the group. + * @target: The target kobject of the symlink to create. + * @link_name: The name of the symlink to create. + */ +int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, + struct kobject *target, const char *link_name) +{ + struct sysfs_dirent *dir_sd; + int error = 0; + + dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); + if (!dir_sd) + return -ENOENT; + + error = sysfs_create_link_sd(dir_sd, target, link_name); + sysfs_put(dir_sd); + + return error; +} +EXPORT_SYMBOL_GPL(sysfs_add_link_to_group); + +/** + * sysfs_remove_link_from_group - remove a symlink from an attribute group. + * @kobj: The kobject containing the group. + * @group_name: The name of the group. + * @link_name: The name of the symlink to remove. + */ +void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, + const char *link_name) +{ + struct sysfs_dirent *dir_sd; + + dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); + if (dir_sd) { + sysfs_hash_and_remove(dir_sd, NULL, link_name); + sysfs_put(dir_sd); + } +} +EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); EXPORT_SYMBOL_GPL(sysfs_create_group); EXPORT_SYMBOL_GPL(sysfs_update_group); diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 3c9eb5624f5e..8c940df97a52 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -21,26 +21,17 @@ #include "sysfs.h" -static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, - const char *name, int warn) +static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, + struct kobject *target, + const char *name, int warn) { - struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; struct sysfs_dirent *sd = NULL; struct sysfs_addrm_cxt acxt; enum kobj_ns_type ns_type; int error; - BUG_ON(!name); - - if (!kobj) - parent_sd = &sysfs_root; - else - parent_sd = kobj->sd; - - error = -EFAULT; - if (!parent_sd) - goto out_put; + BUG_ON(!name || !parent_sd); /* target->sd can go away beneath us but is protected with * sysfs_assoc_lock. Fetch target_sd from it. @@ -95,6 +86,34 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, return error; } +/** + * sysfs_create_link_sd - create symlink to a given object. + * @sd: directory we're creating the link in. + * @target: object we're pointing to. + * @name: name of the symlink. + */ +int sysfs_create_link_sd(struct sysfs_dirent *sd, struct kobject *target, + const char *name) +{ + return sysfs_do_create_link_sd(sd, target, name, 1); +} + +static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, + const char *name, int warn) +{ + struct sysfs_dirent *parent_sd = NULL; + + if (!kobj) + parent_sd = &sysfs_root; + else + parent_sd = kobj->sd; + + if (!parent_sd) + return -EFAULT; + + return sysfs_do_create_link_sd(parent_sd, target, name, warn); +} + /** * sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index d73c0932bbd6..d1e4043eb0c3 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -240,3 +240,5 @@ void unmap_bin_file(struct sysfs_dirent *attr_sd); * symlink.c */ extern const struct inode_operations sysfs_symlink_inode_operations; +int sysfs_create_link_sd(struct sysfs_dirent *sd, struct kobject *target, + const char *name); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 381f06db2fe5..e2cee22f578a 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -181,6 +181,10 @@ int sysfs_merge_group(struct kobject *kobj, const struct attribute_group *grp); void sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp); +int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, + struct kobject *target, const char *link_name); +void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, + const char *link_name); void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr); void sysfs_notify_dirent(struct sysfs_dirent *sd); @@ -326,6 +330,18 @@ static inline void sysfs_unmerge_group(struct kobject *kobj, { } +static inline int sysfs_add_link_to_group(struct kobject *kobj, + const char *group_name, struct kobject *target, + const char *link_name) +{ + return 0; +} + +static inline void sysfs_remove_link_from_group(struct kobject *kobj, + const char *group_name, const char *link_name) +{ +} + static inline void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr) { -- cgit v1.2.3-55-g7522