diff options
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 727 |
1 files changed, 413 insertions, 314 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b643aed8b74..4be408756ad 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -29,29 +29,10 @@ extern struct acpi_device *acpi_root; static const char *dummy_hid = "device"; -/* - * The following ACPI IDs are known to be suitable for representing as - * platform devices. - */ -static const struct acpi_device_id acpi_platform_device_ids[] = { - - { "PNP0D40" }, - - /* Haswell LPSS devices */ - { "INT33C0", 0 }, - { "INT33C1", 0 }, - { "INT33C2", 0 }, - { "INT33C3", 0 }, - { "INT33C4", 0 }, - { "INT33C5", 0 }, - { "INT33C6", 0 }, - { "INT33C7", 0 }, - - { } -}; - static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); +static DEFINE_MUTEX(acpi_scan_lock); +static LIST_HEAD(acpi_scan_handlers_list); DEFINE_MUTEX(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); @@ -61,6 +42,27 @@ struct acpi_device_bus_id{ struct list_head node; }; +void acpi_scan_lock_acquire(void) +{ + mutex_lock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_acquire); + +void acpi_scan_lock_release(void) +{ + mutex_unlock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_release); + +int acpi_scan_add_handler(struct acpi_scan_handler *handler) +{ + if (!handler || !handler->attach) + return -EINVAL; + + list_add_tail(&handler->list_node, &acpi_scan_handlers_list); + return 0; +} + /* * Creates hid/cid(s) string needed for modalias and uevent * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: @@ -115,7 +117,7 @@ static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); */ void acpi_bus_hot_remove_device(void *context) { - struct acpi_eject_event *ej_event = (struct acpi_eject_event *) context; + struct acpi_eject_event *ej_event = context; struct acpi_device *device = ej_event->device; acpi_handle handle = device->handle; acpi_handle temp; @@ -124,23 +126,22 @@ void acpi_bus_hot_remove_device(void *context) acpi_status status = AE_OK; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Hot-removing device %s...\n", dev_name(&device->dev))); + mutex_lock(&acpi_scan_lock); - if (acpi_bus_trim(device)) { - printk(KERN_ERR PREFIX - "Removing device failed\n"); - goto err_out; + /* If there is no handle, the device node has been unregistered. */ + if (!device->handle) { + dev_dbg(&device->dev, "ACPI handle missing\n"); + put_device(&device->dev); + goto out; } - /* device has been freed */ - device = NULL; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Hot-removing device %s...\n", dev_name(&device->dev))); - /* power off device */ - status = acpi_evaluate_object(handle, "_PS3", NULL, NULL); - if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) - printk(KERN_WARNING PREFIX - "Power-off device failed\n"); + acpi_bus_trim(device); + /* Device node has been unregistered. */ + put_device(&device->dev); + device = NULL; if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { arg_list.count = 1; @@ -161,23 +162,46 @@ void acpi_bus_hot_remove_device(void *context) status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) - printk(KERN_WARNING PREFIX - "Eject device failed\n"); - goto err_out; - } + acpi_handle_warn(handle, "Eject failed\n"); - kfree(context); - return; + /* Tell the firmware the hot-remove operation has failed. */ + acpi_evaluate_hotplug_ost(handle, ej_event->event, + ost_code, NULL); + } -err_out: - /* Inform firmware the hot-remove operation has completed w/ error */ - (void) acpi_evaluate_hotplug_ost(handle, - ej_event->event, ost_code, NULL); + out: + mutex_unlock(&acpi_scan_lock); kfree(context); return; } EXPORT_SYMBOL(acpi_bus_hot_remove_device); +static ssize_t real_power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *adev = to_acpi_device(dev); + int state; + int ret; + + ret = acpi_device_get_power(adev, &state); + if (ret) + return ret; + + return sprintf(buf, "%s\n", acpi_power_state_string(state)); +} + +static DEVICE_ATTR(real_power_state, 0444, real_power_state_show, NULL); + +static ssize_t power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *adev = to_acpi_device(dev); + + return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state)); +} + +static DEVICE_ATTR(power_state, 0444, power_state_show, NULL); + static ssize_t acpi_eject_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -191,12 +215,10 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, if ((!count) || (buf[0] != '1')) { return -EINVAL; } -#ifndef FORCE_EJECT - if (acpi_device->driver == NULL) { + if (!acpi_device->driver && !acpi_device->handler) { ret = -ENODEV; goto err; } -#endif status = acpi_get_type(acpi_device->handle, &type); if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) { ret = -ENODEV; @@ -209,6 +231,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, goto err; } + get_device(&acpi_device->dev); ej_event->device = acpi_device; if (acpi_device->flags.eject_pending) { /* event originated from ACPI eject notification */ @@ -221,7 +244,11 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); } - acpi_os_hotplug_execute(acpi_bus_hot_remove_device, (void *)ej_event); + status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); + if (ACPI_FAILURE(status)) { + put_device(&acpi_device->dev); + kfree(ej_event); + } err: return ret; } @@ -369,8 +396,22 @@ static int acpi_device_setup_files(struct acpi_device *dev) * hot-removal function from userland. */ status = acpi_get_handle(dev->handle, "_EJ0", &temp); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { result = device_create_file(&dev->dev, &dev_attr_eject); + if (result) + return result; + } + + if (dev->flags.power_manageable) { + result = device_create_file(&dev->dev, &dev_attr_power_state); + if (result) + return result; + + if (dev->power.flags.power_resources) + result = device_create_file(&dev->dev, + &dev_attr_real_power_state); + } + end: return result; } @@ -380,6 +421,13 @@ static void acpi_device_remove_files(struct acpi_device *dev) acpi_status status; acpi_handle temp; + if (dev->flags.power_manageable) { + device_remove_file(&dev->dev, &dev_attr_power_state); + if (dev->power.flags.power_resources) + device_remove_file(&dev->dev, + &dev_attr_real_power_state); + } + /* * If device has _STR, remove 'description' file */ @@ -448,9 +496,9 @@ const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, const struct device *dev) { struct acpi_device *adev; + acpi_handle handle = ACPI_HANDLE(dev); - if (!ids || !ACPI_HANDLE(dev) - || ACPI_FAILURE(acpi_bus_get_device(ACPI_HANDLE(dev), &adev))) + if (!ids || !handle || acpi_bus_get_device(handle, &adev)) return NULL; return __acpi_match_device(adev, ids); @@ -464,7 +512,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; @@ -472,6 +520,23 @@ static void acpi_free_ids(struct acpi_device *device) kfree(id->id); kfree(id); } + kfree(device->pnp.unique_id); +} + +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; + + 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) @@ -479,7 +544,7 @@ static void acpi_device_release(struct device *dev) struct acpi_device *acpi_dev = to_acpi_device(dev); acpi_free_ids(acpi_dev); - kfree(acpi_dev->pnp.unique_id); + acpi_free_power_resources_lists(acpi_dev); kfree(acpi_dev); } @@ -577,8 +642,9 @@ static int acpi_device_probe(struct device * dev) ret = acpi_device_install_notify_handler(acpi_dev); if (ret) { if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev, - acpi_dev->removal_type); + acpi_drv->ops.remove(acpi_dev); + acpi_dev->driver = NULL; + acpi_dev->driver_data = NULL; return ret; } } @@ -600,7 +666,7 @@ static int acpi_device_remove(struct device * dev) if (acpi_drv->ops.notify) acpi_device_remove_notify_handler(acpi_dev); if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type); + acpi_drv->ops.remove(acpi_dev); } acpi_dev->driver = NULL; acpi_dev->driver_data = NULL; @@ -617,12 +683,25 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; -static int acpi_device_register(struct acpi_device *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; int found = 0; + if (device->handle) { + acpi_status status; + + status = acpi_attach_data(device->handle, acpi_bus_data_handler, + device); + if (ACPI_FAILURE(status)) { + acpi_handle_err(device->handle, + "Unable to attach device data\n"); + return -ENODEV; + } + } + /* * Linkage * ------- @@ -633,11 +712,13 @@ static int acpi_device_register(struct acpi_device *device) INIT_LIST_HEAD(&device->wakeup_list); INIT_LIST_HEAD(&device->physical_node_list); mutex_init(&device->physical_node_lock); + INIT_LIST_HEAD(&device->power_dependent); new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); if (!new_bus_id) { - printk(KERN_ERR PREFIX "Memory allocation error\n"); - return -ENOMEM; + pr_err(PREFIX "Memory allocation error\n"); + result = -ENOMEM; + goto err_detach; } mutex_lock(&acpi_device_lock); @@ -672,11 +753,11 @@ 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; - result = device_register(&device->dev); + device->dev.release = release; + result = device_add(&device->dev); if (result) { dev_err(&device->dev, "Error registering device\n"); - goto end; + goto err; } result = acpi_device_setup_files(device); @@ -686,12 +767,16 @@ static int acpi_device_register(struct acpi_device *device) device->removal_type = ACPI_BUS_REMOVAL_NORMAL; return 0; -end: + + err: mutex_lock(&acpi_device_lock); if (device->parent) list_del(&device->node); list_del(&device->wakeup_list); mutex_unlock(&acpi_device_lock); + + err_detach: + acpi_detach_data(device->handle, acpi_bus_data_handler); return result; } @@ -706,8 +791,20 @@ static void acpi_device_unregister(struct acpi_device *device) acpi_detach_data(device->handle, acpi_bus_data_handler); + acpi_power_add_remove_device(device, false); acpi_device_remove_files(device); - device_unregister(&device->dev); + if (device->remove) + device->remove(device); + + device_del(&device->dev); + /* + * Transition the device to D3cold to drop the reference counts of all + * power resources the device depends on and turn off the ones that have + * no more references. + */ + acpi_device_set_power(device, ACPI_STATE_D3_COLD); + device->handle = NULL; + put_device(&device->dev); } /* -------------------------------------------------------------------------- @@ -794,29 +891,23 @@ EXPORT_SYMBOL(acpi_bus_unregister_driver); -------------------------------------------------------------------------- */ static struct acpi_device *acpi_bus_get_parent(acpi_handle handle) { + struct acpi_device *device = NULL; acpi_status status; - int ret; - struct acpi_device *device; /* * Fixed hardware devices do not appear in the namespace and do not * have handles, but we fabricate acpi_devices for them, so we have * to deal with them specially. */ - if (handle == NULL) + if (!handle) return acpi_root; do { status = acpi_get_parent(handle, &handle); - if (status == AE_NULL_ENTRY) - return NULL; if (ACPI_FAILURE(status)) - return acpi_root; - - ret = acpi_bus_get_device(handle, &device); - if (ret == 0) - return device; - } while (1); + return status == AE_NULL_ENTRY ? NULL : acpi_root; + } while (acpi_bus_get_device(handle, &device)); + return device; } acpi_status @@ -850,52 +941,43 @@ void acpi_bus_data_handler(acpi_handle handle, void *context) return; } -static int acpi_bus_get_perf_flags(struct acpi_device *device) -{ - device->performance.state = ACPI_STATE_UNKNOWN; - return 0; -} - -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 i = 0; + int err = -ENODATA; if (!wakeup) - return AE_BAD_PARAMETER; + return -EINVAL; + + INIT_LIST_HEAD(&wakeup->resources); /* _PRW */ 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 = @@ -904,38 +986,35 @@ 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; - if ((package->package.count - 2) > ACPI_MAX_HANDLES) { - status = AE_NO_MEMORY; + err = acpi_extract_power_resources(package, 2, &wakeup->resources); + if (err) goto out; - } - wakeup->resources.count = package->package.count - 2; - for (i = 0; i < wakeup->resources.count; i++) { - element = &(package->package.elements[i + 2]); - if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { - status = AE_BAD_DATA; - goto out; - } - wakeup->resources.handles[i] = element->reference.handle; - } + 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: kfree(buffer.pointer); - - return status; + return err; } static void acpi_bus_set_run_wake_flags(struct acpi_device *device) @@ -975,17 +1054,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; } @@ -998,20 +1077,73 @@ 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")); } -static void acpi_bus_add_power_resource(acpi_handle handle); +static void acpi_bus_init_power_state(struct acpi_device *device, int state) +{ + struct acpi_device_power_state *ps = &device->power.states[state]; + 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 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) { + int err = acpi_extract_power_resources(package, 0, + &ps->resources); + if (!err) + device->power.flags.power_resources = 1; + } + ACPI_FREE(buffer.pointer); + } -static int acpi_bus_get_power_flags(struct acpi_device *device) + /* Evaluate "_PSx" to see if we can do explicit sets */ + pathname[2] = 'S'; + status = acpi_get_handle(device->handle, pathname, &handle); + if (ACPI_SUCCESS(status)) + ps->flags.explicit_set = 1; + + /* + * State is valid if there are means to put the device into it. + * D3hot is only valid if _PR3 present. + */ + if (!list_empty(&ps->resources) + || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) { + ps->flags.valid = 1; + ps->flags.os_accessible = 1; + } + + ps->power = -1; /* Unknown - driver assigned */ + ps->latency = -1; /* Unknown - driver assigned */ +} + +static void acpi_bus_get_power_flags(struct acpi_device *device) { - acpi_status status = 0; - acpi_handle handle = NULL; - u32 i = 0; + acpi_status status; + acpi_handle handle; + u32 i; + /* Presence of _PS0|_PR0 indicates 'power manageable' */ + status = acpi_get_handle(device->handle, "_PS0", &handle); + if (ACPI_FAILURE(status)) { + status = acpi_get_handle(device->handle, "_PR0", &handle); + if (ACPI_FAILURE(status)) + return; + } + + device->flags.power_manageable = 1; /* * Power Management Flags @@ -1026,40 +1158,10 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) /* * Enumerate supported power management states */ - 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' }; - - /* Evaluate "_PRx" to se if power resources are referenced */ - acpi_evaluate_reference(device->handle, object_name, NULL, - &ps->resources); - if (ps->resources.count) { - 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]); - } - - /* Evaluate "_PSx" to see if we can do explicit sets */ - object_name[2] = 'S'; - status = acpi_get_handle(device->handle, object_name, &handle); - if (ACPI_SUCCESS(status)) - ps->flags.explicit_set = 1; - - /* - * State is valid if there are means to put the device into it. - * D3hot is only valid if _PR3 present. - */ - if (ps->resources.count || - (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) { - ps->flags.valid = 1; - ps->flags.os_accessible = 1; - } + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) + acpi_bus_init_power_state(device, i); - ps->power = -1; /* Unknown - driver assigned */ - 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; @@ -1076,17 +1178,17 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) device->power.flags.power_resources) device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; - acpi_bus_init_power(device); - - return 0; + if (acpi_bus_init_power(device)) { + acpi_free_power_resources_lists(device); + device->flags.power_manageable = 0; + } } -static int acpi_bus_get_flags(struct acpi_device *device) +static void acpi_bus_get_flags(struct acpi_device *device) { acpi_status status = AE_OK; acpi_handle temp = NULL; - /* Presence of _STA indicates 'dynamic_status' */ status = acpi_get_handle(device->handle, "_STA", &temp); if (ACPI_SUCCESS(status)) @@ -1106,21 +1208,6 @@ static int acpi_bus_get_flags(struct acpi_device *device) if (ACPI_SUCCESS(status)) device->flags.ejectable = 1; } - - /* Power resources cannot be power manageable. */ - if (device->device_type == ACPI_BUS_TYPE_POWER) - return 0; - - /* Presence of _PS0|_PR0 indicates 'power manageable' */ - status = acpi_get_handle(device->handle, "_PS0", &temp); - if (ACPI_FAILURE(status)) - status = acpi_get_handle(device->handle, "_PR0", &temp); - if (ACPI_SUCCESS(status)) - device->flags.power_manageable = 1; - - /* TBD: Performance management */ - - return 0; } static void acpi_device_get_busid(struct acpi_device *device) @@ -1319,7 +1406,7 @@ static void acpi_device_set_id(struct acpi_device *device) acpi_add_id(device, ACPI_DOCK_HID); else if (!acpi_ibm_smbus_match(device)) acpi_add_id(device, ACPI_SMBUS_IBM_HID); - else if (!acpi_device_hid(device) && + else if (list_empty(&device->pnp.ids) && ACPI_IS_ROOT_DEVICE(device->parent)) { acpi_add_id(device, ACPI_BUS_HID); /* \_SB, LNXSYBUS */ strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); @@ -1345,32 +1432,32 @@ static void acpi_device_set_id(struct acpi_device *device) } } -static int acpi_device_set_context(struct acpi_device *device) +void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, + int type, unsigned long long sta) { - acpi_status status; - - /* - * Context - * ------- - * Attach this 'struct acpi_device' to the ACPI object. This makes - * resolutions from handle->device very efficient. Fixed hardware - * devices have no handles, so we skip them. - */ - if (!device->handle) - return 0; - - status = acpi_attach_data(device->handle, - acpi_bus_data_handler, device); - if (ACPI_SUCCESS(status)) - return 0; + INIT_LIST_HEAD(&device->pnp.ids); + device->device_type = type; + device->handle = handle; + device->parent = acpi_bus_get_parent(handle); + STRUCT_TO_INT(device->status) = sta; + acpi_device_get_busid(device); + acpi_device_set_id(device); + acpi_bus_get_flags(device); + device->flags.match_driver = false; + device_initialize(&device->dev); + dev_set_uevent_suppress(&device->dev, true); +} - printk(KERN_ERR PREFIX "Error attaching device data\n"); - return -ENODEV; +void acpi_device_add_finalize(struct acpi_device *device) +{ + device->flags.match_driver = true; + dev_set_uevent_suppress(&device->dev, false); + kobject_uevent(&device->dev.kobj, KOBJ_ADD); } static int acpi_add_single_object(struct acpi_device **child, acpi_handle handle, int type, - unsigned long long sta, bool match_driver) + unsigned long long sta) { int result; struct acpi_device *device; @@ -1382,90 +1469,25 @@ static int acpi_add_single_object(struct acpi_device **child, return -ENOMEM; } - INIT_LIST_HEAD(&device->pnp.ids); - device->device_type = type; - device->handle = handle; - device->parent = acpi_bus_get_parent(handle); - STRUCT_TO_INT(device->status) = sta; - - acpi_device_get_busid(device); - - /* - * Flags - * ----- - * Note that we only look for object handles -- cannot evaluate objects - * until we know the device is present and properly initialized. - */ - result = acpi_bus_get_flags(device); - if (result) - goto end; - - /* - * Initialize Device - * ----------------- - * TBD: Synch with Core's enumeration/initialization process. - */ - acpi_device_set_id(device); - - /* - * Power Management - * ---------------- - */ - if (device->flags.power_manageable) { - result = acpi_bus_get_power_flags(device); - if (result) - goto end; - } - - /* - * Wakeup device management - *----------------------- - */ + acpi_init_device_object(device, handle, type, sta); + acpi_bus_get_power_flags(device); acpi_bus_get_wakeup_device_flags(device); - /* - * Performance Management - * ---------------------- - */ - if (device->flags.performance_manageable) { - result = acpi_bus_get_perf_flags(device); - if (result) - goto end; - } - - if ((result = acpi_device_set_context(device))) - goto end; - - device->flags.match_driver = match_driver; - result = acpi_device_register(device); - -end: - if (!result) { - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Adding %s [%s] parent %s\n", dev_name(&device->dev), - (char *) buffer.pointer, - device->parent ? dev_name(&device->parent->dev) : - "(null)")); - kfree(buffer.pointer); - *child = device; - } else + result = acpi_device_add(device, acpi_device_release); + if (result) { acpi_device_release(&device->dev); + return result; + } - return result; -} - -#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); + 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, + device->parent ? dev_name(&device->parent->dev) : "(null)")); + kfree(buffer.pointer); + *child = device; + return 0; } static int acpi_bus_type_and_status(acpi_handle handle, int *type, @@ -1524,25 +1546,29 @@ 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; 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; } - acpi_add_single_object(&device, handle, type, sta, - type == ACPI_BUS_TYPE_POWER); + acpi_add_single_object(&device, handle, type, sta); if (!device) return AE_CTRL_DEPTH; - device->flags.match_driver = true; - out: if (!*return_value) *return_value = device; @@ -1550,31 +1576,68 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } +static int acpi_scan_do_attach_handler(struct acpi_device *device, char *id) +{ + struct acpi_scan_handler *handler; + + list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { + const struct acpi_device_id *devid; + + for (devid = handler->ids; devid->id[0]; devid++) { + int ret; + + if (strcmp((char *)devid->id, id)) + continue; + + ret = handler->attach(device, devid); + if (ret > 0) { + device->handler = handler; + return ret; + } else if (ret < 0) { + return ret; + } + } + } + return 0; +} + +static int acpi_scan_attach_handler(struct acpi_device *device) +{ + struct acpi_hardware_id *hwid; + int ret = 0; + + list_for_each_entry(hwid, &device->pnp.ids, list) { + ret = acpi_scan_do_attach_handler(device, hwid->id); + if (ret) + break; + + } + return ret; +} + static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, void *not_used, void **ret_not_used) { - acpi_status status = AE_OK; struct acpi_device *device; unsigned long long sta_not_used; - int type_not_used; + int ret; /* * Ignore errors ignored by acpi_bus_check_add() to avoid terminating * namespace walks prematurely. */ - if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) + if (acpi_bus_type_and_status(handle, &ret, &sta_not_used)) return AE_OK; if (acpi_bus_get_device(handle, &device)) return AE_CTRL_DEPTH; - if (!acpi_match_device_ids(device, acpi_platform_device_ids)) { - /* This is a known good platform device. */ - acpi_create_platform_device(device); - } else if (device_attach(&device->dev) < 0) { - status = AE_CTRL_DEPTH; - } - return status; + ret = acpi_scan_attach_handler(device); + if (ret) + return ret > 0 ? AE_OK : AE_CTRL_DEPTH; + + ret = device_attach(&device->dev); + return ret >= 0 ? AE_OK : AE_CTRL_DEPTH; } /** @@ -1588,23 +1651,25 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, * there has been a real error. There just have been no suitable ACPI objects * in the table trunk from which the kernel could create a device and add an * appropriate driver. + * + * Must be called under acpi_scan_lock. */ int acpi_bus_scan(acpi_handle handle) { void *device = NULL; + int error = 0; if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_check_add, NULL, NULL, &device); if (!device) - return -ENODEV; - - if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) + error = -ENODEV; + else if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_device_attach, NULL, NULL, NULL); - return 0; + return error; } EXPORT_SYMBOL(acpi_bus_scan); @@ -1614,8 +1679,17 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, struct acpi_device *device = NULL; if (!acpi_bus_get_device(handle, &device)) { + struct acpi_scan_handler *dev_handler = device->handler; + device->removal_type = ACPI_BUS_REMOVAL_EJECT; - device_release_driver(&device->dev); + if (dev_handler) { + if (dev_handler->detach) + dev_handler->detach(device); + + device->handler = NULL; + } else { + device_release_driver(&device->dev); + } } return AE_OK; } @@ -1631,7 +1705,13 @@ static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -int acpi_bus_trim(struct acpi_device *start) +/** + * acpi_bus_trim - Remove ACPI device node and all of its descendants + * @start: Root of the ACPI device nodes subtree to remove. + * + * Must be called under acpi_scan_lock. + */ +void acpi_bus_trim(struct acpi_device *start) { /* * Execute acpi_bus_device_detach() as a post-order callback to detach @@ -1647,32 +1727,45 @@ int acpi_bus_trim(struct acpi_device *start) acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL, acpi_bus_remove, NULL, NULL); acpi_bus_remove(start->handle, 0, NULL, NULL); - return 0; } EXPORT_SYMBOL_GPL(acpi_bus_trim); static int acpi_bus_scan_fixed(void) { int result = 0; - struct acpi_device *device = NULL; /* * Enumerate all fixed-feature devices. */ - if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) { + if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) { + struct acpi_device *device = NULL; + result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_POWER_BUTTON, - ACPI_STA_DEFAULT, true); + ACPI_STA_DEFAULT); + if (result) + return result; + + result = device_attach(&device->dev); + if (result < 0) + return result; + device_init_wakeup(&device->dev, true); } - if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) { + if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) { + struct acpi_device *device = NULL; + result = acpi_add_single_object(&device, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, - ACPI_STA_DEFAULT, true); + ACPI_STA_DEFAULT); + if (result) + return result; + + result = device_attach(&device->dev); } - return result; + return result < 0 ? result : 0; } int __init acpi_scan_init(void) @@ -1685,30 +1778,36 @@ int __init acpi_scan_init(void) printk(KERN_ERR PREFIX "Could not register bus type\n"); } - acpi_power_init(); acpi_pci_root_init(); + acpi_pci_link_init(); + acpi_platform_init(); + acpi_csrt_init(); + acpi_container_init(); acpi_pci_slot_init(); + mutex_lock(&acpi_scan_lock); /* * Enumerate devices in the ACPI namespace. */ result = acpi_bus_scan(ACPI_ROOT_OBJECT); if (result) - return result; + goto out; result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root); if (result) - return result; + goto out; result = acpi_bus_scan_fixed(); if (result) { acpi_device_unregister(acpi_root); - return result; + goto out; } acpi_update_all_gpes(); acpi_pci_root_hp_init(); - return 0; + out: + mutex_unlock(&acpi_scan_lock); + return result; } |