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 --- drivers/acpi/internal.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/acpi/internal.h') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae14..b79b4258bd6 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -38,6 +38,7 @@ static inline void acpi_debugfs_init(void) { return; } Power Resource -------------------------------------------------------------------------- */ int acpi_power_init(void); +void acpi_power_add_remove_device(struct acpi_device *adev, bool add); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); int acpi_power_get_inferred_state(struct acpi_device *device, int *state); -- cgit v1.2.3-70-g09d2 From 82c7d5efaadf99fb4a26500cd5b59b6fd7659772 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:05 +0100 Subject: ACPI / scan: Treat power resources in a special way ACPI power resources need to be treated in a special way by the namespace scanning code, because they need to be ready to use as soon as they have been discovered (even before registering ACPI device nodes using them for power management). For this reason, it doesn't make sense to separate the preparation of struct acpi_device objects representing them in the device hierarchy from the creation of struct acpi_power_resource objects actually used for power resource manipulation. Accordingly, it doesn't make sense to define non-empty .add() and .remove() callbacks in the power resources "driver" (in fact, it is questionable whether or not it is useful to register such a "driver" at all). Rearrange the code in scan.c and power.c so that power resources are initialized entirely by one routine, acpi_add_power_resource(), that also prepares their struct acpi_device objects and registers them with the driver core, telling it to use a special release routine, acpi_release_power_resource(), for removing objects that represent power resources from memory. Make the ACPI namespace scanning code in scan.c always use acpi_add_power_resource() for preparing and registering objects that represent power resources. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 13 ++++ drivers/acpi/power.c | 157 ++++++++++++++++++++++-------------------------- drivers/acpi/scan.c | 47 ++++++--------- 3 files changed, 102 insertions(+), 115 deletions(-) (limited to 'drivers/acpi/internal.h') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index b79b4258bd6..ce6cb24de8c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -34,10 +34,23 @@ int acpi_debugfs_init(void); static inline void acpi_debugfs_init(void) { return; } #endif +/* -------------------------------------------------------------------------- + Device Node Initialization / Removal + -------------------------------------------------------------------------- */ +#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) + +int acpi_device_register(struct acpi_device *device, + void (*release)(struct device *)); +void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, + int type, unsigned long long sta); +void acpi_free_ids(struct acpi_device *device); + /* -------------------------------------------------------------------------- Power Resource -------------------------------------------------------------------------- */ int acpi_power_init(void); +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, int enable, int sleep_state, int dev_state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 659386c4f0c..b12933fd2e5 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -58,8 +58,7 @@ ACPI_MODULE_NAME("power"); #define ACPI_POWER_RESOURCE_STATE_ON 0x01 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF -static int acpi_power_add(struct acpi_device *device); -static int acpi_power_remove(struct acpi_device *device, int type); +static inline int acpi_power_add(struct acpi_device *device) { return 0; } static const struct acpi_device_id power_device_ids[] = { {ACPI_POWER_HID, 0}, @@ -76,10 +75,7 @@ static struct acpi_driver acpi_power_driver = { .name = "power", .class = ACPI_POWER_CLASS, .ids = power_device_ids, - .ops = { - .add = acpi_power_add, - .remove = acpi_power_remove, - }, + .ops.add = acpi_power_add, .drv.pm = &acpi_power_pm, }; @@ -90,9 +86,9 @@ struct acpi_power_dependent_device { }; struct acpi_power_resource { - struct acpi_device *device; + struct acpi_device device; struct list_head dependent; - acpi_bus_id name; + char *name; u32 system_level; u32 order; unsigned int ref_count; @@ -105,28 +101,14 @@ static struct list_head acpi_power_resource_list; Power Resource Management -------------------------------------------------------------------------- */ -static int -acpi_power_get_context(acpi_handle handle, - struct acpi_power_resource **resource) +static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { - int result = 0; - struct acpi_device *device = NULL; - - - if (!resource) - return -ENODEV; - - result = acpi_bus_get_device(handle, &device); - if (result) { - printk(KERN_WARNING PREFIX "Getting context [%p]\n", handle); - return result; - } + struct acpi_device *device; - *resource = acpi_driver_data(device); - if (!*resource) - return -ENODEV; + if (acpi_bus_get_device(handle, &device)) + return NULL; - return 0; + return container_of(device, struct acpi_power_resource, device); } static int acpi_power_get_state(acpi_handle handle, int *state) @@ -171,9 +153,9 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) acpi_handle handle = list->handles[i]; int result; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + resource = acpi_power_get_context(handle); + if (!resource) + return -ENODEV; mutex_lock(&resource->resource_lock); @@ -226,12 +208,12 @@ static int __acpi_power_on(struct acpi_power_resource *resource) { acpi_status status = AE_OK; - status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); + status = acpi_evaluate_object(resource->device.handle, "_ON", NULL, NULL); if (ACPI_FAILURE(status)) return -ENODEV; /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D0; + resource->device.power.state = ACPI_STATE_D0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", resource->name)); @@ -242,11 +224,11 @@ static int __acpi_power_on(struct acpi_power_resource *resource) static int acpi_power_on(acpi_handle handle) { int result = 0; - struct acpi_power_resource *resource = NULL; + struct acpi_power_resource *resource; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + resource = acpi_power_get_context(handle); + if (!resource) + return -ENODEV; mutex_lock(&resource->resource_lock); @@ -275,11 +257,11 @@ static int acpi_power_off(acpi_handle handle) { int result = 0; acpi_status status = AE_OK; - struct acpi_power_resource *resource = NULL; + struct acpi_power_resource *resource; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + resource = acpi_power_get_context(handle); + if (!resource) + return -ENODEV; mutex_lock(&resource->resource_lock); @@ -297,12 +279,12 @@ static int acpi_power_off(acpi_handle handle) goto unlock; } - status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL); + status = acpi_evaluate_object(resource->device.handle, "_OFF", NULL, NULL); if (ACPI_FAILURE(status)) { result = -ENODEV; } else { /* Update the power resource's _device_ power state */ - resource->device->power.state = ACPI_STATE_D3; + resource->device.power.state = ACPI_STATE_D3; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned off\n", @@ -350,7 +332,11 @@ static void acpi_power_add_dependent(acpi_handle rhandle, struct acpi_power_dependent_device *dep; struct acpi_power_resource *resource; - if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) + if (!rhandle || !adev) + return; + + resource = acpi_power_get_context(rhandle); + if (!resource) return; mutex_lock(&resource->resource_lock); @@ -378,7 +364,11 @@ static void acpi_power_remove_dependent(acpi_handle rhandle, struct acpi_power_resource *resource; struct work_struct *work = NULL; - if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) + if (!rhandle || !adev) + return; + + resource = acpi_power_get_context(rhandle); + if (!resource) return; mutex_lock(&resource->resource_lock); @@ -648,46 +638,53 @@ int acpi_power_transition(struct acpi_device *device, int state) return result; } -/* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ +static void acpi_release_power_resource(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct acpi_power_resource *resource; + + acpi_free_ids(device); + resource = container_of(device, struct acpi_power_resource, device); + kfree(resource); +} -static int acpi_power_add(struct acpi_device *device) +void acpi_add_power_resource(acpi_handle handle) { - int result = 0, state; - acpi_status status = AE_OK; - struct acpi_power_resource *resource = NULL; + struct acpi_power_resource *resource; + struct acpi_device *device = NULL; union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; + acpi_status status; + int state, result = -ENODEV; + acpi_bus_get_device(handle, &device); + if (device) + return; - if (!device) - return -EINVAL; - - resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL); + resource = kzalloc(sizeof(*resource), GFP_KERNEL); if (!resource) - return -ENOMEM; + return; - resource->device = device; + device = &resource->device; + acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, + ACPI_STA_DEFAULT); mutex_init(&resource->resource_lock); INIT_LIST_HEAD(&resource->dependent); - strcpy(resource->name, device->pnp.bus_id); + resource->name = device->pnp.bus_id; strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); - device->driver_data = resource; /* Evalute the object to get the system level and resource order. */ - status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } + status = acpi_evaluate_object(handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) + goto out; + resource->system_level = acpi_object.power_resource.system_level; resource->order = acpi_object.power_resource.resource_order; - result = acpi_power_get_state(device->handle, &state); + result = acpi_power_get_state(handle, &state); if (result) - goto end; + goto out; switch (state) { case ACPI_POWER_RESOURCE_STATE_ON: @@ -698,34 +695,24 @@ static int acpi_power_add(struct acpi_device *device) break; default: device->power.state = ACPI_STATE_UNKNOWN; - break; } printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), state ? "on" : "off"); - end: + device->flags.match_driver = true; + result = acpi_device_register(device, acpi_release_power_resource); + + out: if (result) - kfree(resource); + acpi_release_power_resource(&device->dev); - return result; + return; } -static int acpi_power_remove(struct acpi_device *device, int type) -{ - struct acpi_power_resource *resource; - - if (!device) - return -EINVAL; - - resource = acpi_driver_data(device); - if (!resource) - return -EINVAL; - - kfree(resource); - - return 0; -} +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ #ifdef CONFIG_PM_SLEEP static int acpi_power_resume(struct device *dev) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 02629a810c0..952b08af91d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -464,7 +464,7 @@ int acpi_match_device_ids(struct acpi_device *device, } EXPORT_SYMBOL(acpi_match_device_ids); -static void acpi_free_ids(struct acpi_device *device) +void acpi_free_ids(struct acpi_device *device) { struct acpi_hardware_id *id, *tmp; @@ -617,7 +617,8 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; -static int acpi_device_register(struct acpi_device *device) +int acpi_device_register(struct acpi_device *device, + void (*release)(struct device *)) { int result; struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; @@ -686,7 +687,7 @@ static int acpi_device_register(struct acpi_device *device) if (device->parent) device->dev.parent = &device->parent->dev; device->dev.bus = &acpi_bus_type; - device->dev.release = &acpi_device_release; + device->dev.release = release; result = device_register(&device->dev); if (result) { dev_err(&device->dev, "Error registering device\n"); @@ -1022,18 +1023,12 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) "error in _DSW or _PSW evaluation\n")); } -static void acpi_bus_add_power_resource(acpi_handle handle); - static void acpi_bus_get_power_flags(struct acpi_device *device) { acpi_status status = 0; acpi_handle handle = NULL; u32 i = 0; - /* Power resources cannot be power manageable. */ - if (device->device_type == ACPI_BUS_TYPE_POWER) - return; - /* Presence of _PS0|_PR0 indicates 'power manageable' */ status = acpi_get_handle(device->handle, "_PS0", &handle); if (ACPI_FAILURE(status)) { @@ -1068,8 +1063,10 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) int j; device->power.flags.power_resources = 1; - for (j = 0; j < ps->resources.count; j++) - acpi_bus_add_power_resource(ps->resources.handles[j]); + for (j = 0; j < ps->resources.count; j++) { + acpi_handle rhandle = ps->resources.handles[j]; + acpi_add_power_resource(rhandle); + } } /* Evaluate "_PSx" to see if we can do explicit sets */ @@ -1358,9 +1355,8 @@ static void acpi_device_set_id(struct acpi_device *device) } } -static void acpi_init_device_object(struct acpi_device *device, - acpi_handle handle, - int type, unsigned long long sta) +void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, + int type, unsigned long long sta) { INIT_LIST_HEAD(&device->pnp.ids); device->device_type = type; @@ -1391,7 +1387,7 @@ static int acpi_add_single_object(struct acpi_device **child, acpi_bus_get_wakeup_device_flags(device); device->flags.match_driver = match_driver; - result = acpi_device_register(device); + result = acpi_device_register(device, acpi_device_release); if (result) { acpi_device_release(&device->dev); return result; @@ -1407,19 +1403,6 @@ static int acpi_add_single_object(struct acpi_device **child, return 0; } -#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ - ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) - -static void acpi_bus_add_power_resource(acpi_handle handle) -{ - struct acpi_device *device = NULL; - - acpi_bus_get_device(handle, &device); - if (!device) - acpi_add_single_object(&device, handle, ACPI_BUS_TYPE_POWER, - ACPI_STA_DEFAULT, true); -} - static int acpi_bus_type_and_status(acpi_handle handle, int *type, unsigned long long *sta) { @@ -1476,6 +1459,11 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, if (result) return AE_OK; + if (type == ACPI_BUS_TYPE_POWER) { + acpi_add_power_resource(handle); + return AE_OK; + } + if (!(sta & ACPI_STA_DEVICE_PRESENT) && !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { struct acpi_device_wakeup wakeup; @@ -1488,8 +1476,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_CTRL_DEPTH; } - acpi_add_single_object(&device, handle, type, sta, - type == ACPI_BUS_TYPE_POWER); + acpi_add_single_object(&device, handle, type, sta, false); if (!device) return AE_CTRL_DEPTH; -- cgit v1.2.3-70-g09d2 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 'drivers/acpi/internal.h') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ce6cb24de8c..e28068a765a 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 06ad05288af..22a3d00d035 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 c7ea9c2649a..d557868c008 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 29a1badfca5..32dc679d2c7 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-70-g09d2 From ef85bdbec444b42775a18580c6bfe1307a63ef0f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:07 +0100 Subject: ACPI / scan: Consolidate extraction of power resources lists The lists of ACPI power resources are currently extracted in two different ways, one for wakeup power resources and one for power resources that device power states depend on. There is no reason why it should be done differently in those two cases, so introduce a common routine for extracting power resources lists from data returned by AML, acpi_extract_power_resources(), and make the namespace scanning code use it for both wakeup and device power states power resources. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 4 +++- drivers/acpi/power.c | 32 ++++++++++++++++++++++++++++++- drivers/acpi/scan.c | 51 ++++++++++++++++++++----------------------------- 3 files changed, 55 insertions(+), 32 deletions(-) (limited to 'drivers/acpi/internal.h') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e28068a765a..c35435e3d76 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -50,8 +50,10 @@ 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); +acpi_status acpi_extract_power_resources(union acpi_object *package, + unsigned int start, + 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 242feca231e..4b93c97aff9 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -97,7 +97,8 @@ 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) +static 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; @@ -132,6 +133,35 @@ void acpi_power_resources_list_free(struct list_head *list) } } +acpi_status acpi_extract_power_resources(union acpi_object *package, + unsigned int start, + struct list_head *list) +{ + acpi_status status = AE_OK; + unsigned int i; + + for (i = start; i < package->package.count; i++) { + union acpi_object *element = &package->package.elements[i]; + acpi_handle rhandle; + + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { + status = AE_BAD_DATA; + break; + } + rhandle = element->reference.handle; + if (!rhandle) { + status = AE_NULL_ENTRY; + break; + } + acpi_add_power_resource(rhandle); + acpi_power_resources_list_add(rhandle, list); + } + if (ACPI_FAILURE(status)) + acpi_power_resources_list_free(list); + + return status; +} + static int acpi_power_get_state(acpi_handle handle, int *state) { acpi_status status = AE_OK; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8da315418d9..d80df969f64 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -900,7 +900,6 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, union acpi_object *package = NULL; union acpi_object *element = NULL; acpi_status status; - int i = 0; if (!wakeup) return AE_BAD_PARAMETER; @@ -953,18 +952,9 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, } wakeup->sleep_state = element->integer.value; - 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; - } - rhandle = element->reference.handle; - acpi_add_power_resource(rhandle); - acpi_power_resources_list_add(rhandle, &wakeup->resources); - } + status = acpi_extract_power_resources(package, 2, &wakeup->resources); + if (ACPI_FAILURE(status)) + goto out; acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); @@ -1021,7 +1011,6 @@ 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; } @@ -1044,30 +1033,32 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) static void acpi_bus_init_power_state(struct acpi_device *device, int state) { struct acpi_device_power_state *ps = &device->power.states[state]; - char object_name[5] = { '_', 'P', 'R', '0' + state, '\0' }; - struct acpi_handle_list resources; + char pathname[5] = { '_', 'P', 'R', '0' + state, '\0' }; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_handle handle; acpi_status status; INIT_LIST_HEAD(&ps->resources); - /* Evaluate "_PRx" to se if power resources are referenced */ - acpi_evaluate_reference(device->handle, object_name, NULL, &resources); - if (resources.count) { - int j; - - device->power.flags.power_resources = 1; - 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); + /* Evaluate "_PRx" to get referenced power resources */ + status = acpi_evaluate_object(device->handle, pathname, NULL, &buffer); + if (ACPI_SUCCESS(status)) { + union acpi_object *package = buffer.pointer; + + if (buffer.length && package + && package->type == ACPI_TYPE_PACKAGE + && package->package.count) { + status = acpi_extract_power_resources(package, 0, + &ps->resources); + if (ACPI_SUCCESS(status)) + device->power.flags.power_resources = 1; } + ACPI_FREE(buffer.pointer); } /* Evaluate "_PSx" to see if we can do explicit sets */ - object_name[2] = 'S'; - status = acpi_get_handle(device->handle, object_name, &handle); + pathname[2] = 'S'; + status = acpi_get_handle(device->handle, pathname, &handle); if (ACPI_SUCCESS(status)) ps->flags.explicit_set = 1; @@ -1075,7 +1066,7 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) * State is valid if there are means to put the device into it. * D3hot is only valid if _PR3 present. */ - if (resources.count + if (!list_empty(&ps->resources) || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) { ps->flags.valid = 1; ps->flags.os_accessible = 1; -- cgit v1.2.3-70-g09d2 From e88c9c603b2ad0cd0fbe90afedba3f1becbbeb79 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:07 +0100 Subject: ACPI: Take power resource initialization errors into account Some ACPI power resource initialization errors, like memory allocation errors, are not taken into account appropriately in some cases, which may lead to a device having an incomplete list of power resources that one of its power states depends on, for one example. Rework the power resource initialization and namespace scanning code so that power resource initialization errors are treated more seriously. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 7 +++--- drivers/acpi/power.c | 44 +++++++++++++++++++++----------------- drivers/acpi/scan.c | 57 ++++++++++++++++++++++--------------------------- 3 files changed, 53 insertions(+), 55 deletions(-) (limited to 'drivers/acpi/internal.h') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index c35435e3d76..8a6c67c9da4 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -51,10 +51,9 @@ void acpi_free_ids(struct acpi_device *device); -------------------------------------------------------------------------- */ int acpi_power_init(void); void acpi_power_resources_list_free(struct list_head *list); -acpi_status acpi_extract_power_resources(union acpi_object *package, - unsigned int start, - struct list_head *list); -void acpi_add_power_resource(acpi_handle handle); +int acpi_extract_power_resources(union acpi_object *package, unsigned int start, + struct list_head *list); +int 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, int enable, int sleep_state, int dev_state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 4b93c97aff9..1600f753faf 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -97,18 +97,18 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) return container_of(device, struct acpi_power_resource, device); } -static void acpi_power_resources_list_add(acpi_handle handle, - struct list_head *list) +static int 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; + return -EINVAL; entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) - return; + return -ENOMEM; entry->resource = resource; if (!list_empty(list)) { @@ -117,10 +117,11 @@ static void acpi_power_resources_list_add(acpi_handle handle, list_for_each_entry(e, list, node) if (e->resource->order > resource->order) { list_add_tail(&entry->node, &e->node); - return; + return 0; } } list_add_tail(&entry->node, list); + return 0; } void acpi_power_resources_list_free(struct list_head *list) @@ -133,33 +134,37 @@ void acpi_power_resources_list_free(struct list_head *list) } } -acpi_status acpi_extract_power_resources(union acpi_object *package, - unsigned int start, - struct list_head *list) +int acpi_extract_power_resources(union acpi_object *package, unsigned int start, + struct list_head *list) { - acpi_status status = AE_OK; unsigned int i; + int err = 0; for (i = start; i < package->package.count; i++) { union acpi_object *element = &package->package.elements[i]; acpi_handle rhandle; if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { - status = AE_BAD_DATA; + err = -ENODATA; break; } rhandle = element->reference.handle; if (!rhandle) { - status = AE_NULL_ENTRY; + err = -ENODEV; break; } - acpi_add_power_resource(rhandle); - acpi_power_resources_list_add(rhandle, list); + err = acpi_add_power_resource(rhandle); + if (err) + break; + + err = acpi_power_resources_list_add(rhandle, list); + if (err) + break; } - if (ACPI_FAILURE(status)) + if (err) acpi_power_resources_list_free(list); - return status; + return err; } static int acpi_power_get_state(acpi_handle handle, int *state) @@ -662,7 +667,7 @@ static void acpi_release_power_resource(struct device *dev) kfree(resource); } -void acpi_add_power_resource(acpi_handle handle) +int acpi_add_power_resource(acpi_handle handle) { struct acpi_power_resource *resource; struct acpi_device *device = NULL; @@ -673,11 +678,11 @@ void acpi_add_power_resource(acpi_handle handle) acpi_bus_get_device(handle, &device); if (device) - return; + return 0; resource = kzalloc(sizeof(*resource), GFP_KERNEL); if (!resource) - return; + return -ENOMEM; device = &resource->device; acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, @@ -712,10 +717,11 @@ void acpi_add_power_resource(acpi_handle handle) mutex_lock(&power_resource_list_lock); list_add(&resource->list_node, &acpi_power_resource_list); mutex_unlock(&power_resource_list_lock); - return; + return 0; err: acpi_release_power_resource(&device->dev); + return result; } #ifdef CONFIG_ACPI_SLEEP diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d80df969f64..0b6a6b4febd 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -892,17 +892,17 @@ void acpi_bus_data_handler(acpi_handle handle, void *context) return; } -static acpi_status -acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, - struct acpi_device_wakeup *wakeup) +static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, + struct acpi_device_wakeup *wakeup) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *package = NULL; union acpi_object *element = NULL; acpi_status status; + int err = -ENODATA; if (!wakeup) - return AE_BAD_PARAMETER; + return -EINVAL; INIT_LIST_HEAD(&wakeup->resources); @@ -910,29 +910,25 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, status = acpi_evaluate_object(handle, "_PRW", NULL, &buffer); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRW")); - return status; + return err; } package = (union acpi_object *)buffer.pointer; - if (!package || (package->package.count < 2)) { - status = AE_BAD_DATA; + if (!package || package->package.count < 2) goto out; - } element = &(package->package.elements[0]); - if (!element) { - status = AE_BAD_DATA; + if (!element) goto out; - } + if (element->type == ACPI_TYPE_PACKAGE) { if ((element->package.count < 2) || (element->package.elements[0].type != ACPI_TYPE_LOCAL_REFERENCE) - || (element->package.elements[1].type != ACPI_TYPE_INTEGER)) { - status = AE_BAD_DATA; + || (element->package.elements[1].type != ACPI_TYPE_INTEGER)) goto out; - } + wakeup->gpe_device = element->package.elements[0].reference.handle; wakeup->gpe_number = @@ -941,27 +937,24 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, wakeup->gpe_device = NULL; wakeup->gpe_number = element->integer.value; } else { - status = AE_BAD_DATA; goto out; } element = &(package->package.elements[1]); - if (element->type != ACPI_TYPE_INTEGER) { - status = AE_BAD_DATA; + if (element->type != ACPI_TYPE_INTEGER) goto out; - } + wakeup->sleep_state = element->integer.value; - status = acpi_extract_power_resources(package, 2, &wakeup->resources); - if (ACPI_FAILURE(status)) + err = acpi_extract_power_resources(package, 2, &wakeup->resources); + if (err) goto out; acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); out: kfree(buffer.pointer); - - return status; + return err; } static void acpi_bus_set_run_wake_flags(struct acpi_device *device) @@ -1001,17 +994,17 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) { acpi_handle temp; acpi_status status = 0; - int psw_error; + int err; /* Presence of _PRW indicates wake capable */ status = acpi_get_handle(device->handle, "_PRW", &temp); if (ACPI_FAILURE(status)) return; - status = acpi_bus_extract_wakeup_device_power_package(device->handle, - &device->wakeup); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Extracting _PRW package")); + err = acpi_bus_extract_wakeup_device_power_package(device->handle, + &device->wakeup); + if (err) { + dev_err(&device->dev, "_PRW evaluation error: %d\n", err); return; } @@ -1024,8 +1017,8 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) * So it is necessary to call _DSW object first. Only when it is not * present will the _PSW object used. */ - psw_error = acpi_device_sleep_wake(device, 0, 0, 0); - if (psw_error) + err = acpi_device_sleep_wake(device, 0, 0, 0); + if (err) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in _DSW or _PSW evaluation\n")); } @@ -1048,9 +1041,9 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) if (buffer.length && package && package->type == ACPI_TYPE_PACKAGE && package->package.count) { - status = acpi_extract_power_resources(package, 0, - &ps->resources); - if (ACPI_SUCCESS(status)) + int err = acpi_extract_power_resources(package, 0, + &ps->resources); + if (!err) device->power.flags.power_resources = 1; } ACPI_FREE(buffer.pointer); -- cgit v1.2.3-70-g09d2 From 0596a52b8357b25185e06af32973225baeb7196a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:07 +0100 Subject: ACPI: Use system level attribute of wakeup power resources The system level attribute of ACPI power resources is the lowest system sleep level (S0, S2 etc.) in which the given resource can be "on" (ACPI 5.0, Section 7.1). On the other hand, wakeup power resources have to be "on" for devices depending on them to be able to signal wakeup. Therefore devices cannot wake up the system from sleep states higher than the minimum of the system level attributes of their wakeup power resources. Use the wakeup power resources' system level values to get the deepest system sleep state (highest system sleep level) the given device can wake up the system from. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 1 + drivers/acpi/power.c | 14 ++++++++++++++ drivers/acpi/scan.c | 11 +++++++++++ 3 files changed, 26 insertions(+) (limited to 'drivers/acpi/internal.h') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 8a6c67c9da4..07f61dbd813 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -55,6 +55,7 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start, struct list_head *list); int acpi_add_power_resource(acpi_handle handle); void acpi_power_add_remove_device(struct acpi_device *adev, bool add); +int acpi_power_min_system_level(struct list_head *list); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); int acpi_power_get_inferred_state(struct acpi_device *device, int *state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 1600f753faf..089a7c39348 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -429,6 +429,20 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add) } } +int acpi_power_min_system_level(struct list_head *list) +{ + struct acpi_power_resource_entry *entry; + int system_level = 5; + + list_for_each_entry(entry, list, node) { + struct acpi_power_resource *resource = entry->resource; + + if (system_level > resource->system_level) + system_level = resource->system_level; + } + return system_level; +} + /* -------------------------------------------------------------------------- Device Power Management -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0b6a6b4febd..1fc57a349a3 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -950,6 +950,17 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, if (err) goto out; + if (!list_empty(&wakeup->resources)) { + int sleep_state; + + sleep_state = acpi_power_min_system_level(&wakeup->resources); + if (sleep_state < wakeup->sleep_state) { + acpi_handle_warn(handle, "Overriding _PRW sleep state " + "(S%d) by S%d from power resources\n", + (int)wakeup->sleep_state, sleep_state); + wakeup->sleep_state = sleep_state; + } + } acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); out: -- cgit v1.2.3-70-g09d2 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 'drivers/acpi/internal.h') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 6c9b16c1666..01708a16536 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 8be4b29e38a..8bca7465c78 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 07f61dbd813..1f004f35bc6 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 71eceb99d41..fca1b9cb27d 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-70-g09d2 From cf860be639d86ed77af179c925085ae0721ae602 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 24 Jan 2013 12:49:49 +0100 Subject: ACPI / scan: Prevent device add uevents from racing with user space ACPI core adds sysfs device files after the given devices have been registered with device_register(), which is not appropriate, because it may lead to race conditions with user space tools using those files. Fix the problem by delaying the KOBJ_ADD uevent for ACPI devices until after all of the devices' sysfs files have been created. This also fixes a use-after-free in acpi_device_unregister(). Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- drivers/acpi/internal.h | 5 +++-- drivers/acpi/power.c | 3 ++- drivers/acpi/scan.c | 20 +++++++++++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/acpi/internal.h') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 1f004f35bc6..c5a61cd6c1a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -40,10 +40,11 @@ static inline void acpi_debugfs_init(void) { return; } #define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) -int acpi_device_register(struct acpi_device *device, - void (*release)(struct device *)); +int acpi_device_add(struct acpi_device *device, + void (*release)(struct device *)); void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, unsigned long long sta); +void acpi_device_add_finalize(struct acpi_device *device); void acpi_free_ids(struct acpi_device *device); /* -------------------------------------------------------------------------- diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 6db261c237d..3f16dd4db23 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -721,13 +721,14 @@ int acpi_add_power_resource(acpi_handle handle) acpi_device_bid(device), state ? "on" : "off"); device->flags.match_driver = true; - result = acpi_device_register(device, acpi_release_power_resource); + result = acpi_device_add(device, acpi_release_power_resource); if (result) goto err; mutex_lock(&power_resource_list_lock); list_add(&resource->list_node, &acpi_power_resource_list); mutex_unlock(&power_resource_list_lock); + acpi_device_add_finalize(device); return 0; err: diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1fc57a349a3..8b3b18846c8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -634,8 +634,8 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; -int acpi_device_register(struct acpi_device *device, - void (*release)(struct device *)) +int acpi_device_add(struct acpi_device *device, + void (*release)(struct device *)) { int result; struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; @@ -705,7 +705,7 @@ int acpi_device_register(struct acpi_device *device, device->dev.parent = &device->parent->dev; device->dev.bus = &acpi_bus_type; device->dev.release = release; - result = device_register(&device->dev); + result = device_add(&device->dev); if (result) { dev_err(&device->dev, "Error registering device\n"); goto err; @@ -744,12 +744,13 @@ static void acpi_device_unregister(struct acpi_device *device) acpi_power_add_remove_device(device, false); acpi_device_remove_files(device); - device_unregister(&device->dev); + device_del(&device->dev); /* * Drop the reference counts of all power resources the device depends * on and turn off the ones that have no more references. */ acpi_power_transition(device, ACPI_STATE_D3_COLD); + put_device(&device->dev); } /* -------------------------------------------------------------------------- @@ -1391,6 +1392,14 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_device_get_busid(device); acpi_device_set_id(device); acpi_bus_get_flags(device); + device_initialize(&device->dev); + dev_set_uevent_suppress(&device->dev, true); +} + +void acpi_device_add_finalize(struct acpi_device *device) +{ + dev_set_uevent_suppress(&device->dev, false); + kobject_uevent(&device->dev.kobj, KOBJ_ADD); } static int acpi_add_single_object(struct acpi_device **child, @@ -1412,13 +1421,14 @@ static int acpi_add_single_object(struct acpi_device **child, acpi_bus_get_wakeup_device_flags(device); device->flags.match_driver = match_driver; - result = acpi_device_register(device, acpi_device_release); + result = acpi_device_add(device, acpi_device_release); if (result) { acpi_device_release(&device->dev); return result; } acpi_power_add_remove_device(device, true); + acpi_device_add_finalize(device); acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Added %s [%s] parent %s\n", dev_name(&device->dev), (char *) buffer.pointer, -- cgit v1.2.3-70-g09d2