diff options
Diffstat (limited to 'drivers/acpi/scan.c')
| -rw-r--r-- | drivers/acpi/scan.c | 2943 |
1 files changed, 1934 insertions, 1009 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c6db591479d..f775fa0d850 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -4,756 +4,1606 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/slab.h> +#include <linux/kernel.h> #include <linux/acpi.h> +#include <linux/signal.h> +#include <linux/kthread.h> +#include <linux/dmi.h> +#include <linux/nls.h> -#include <acpi/acpi_drivers.h> -#include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ +#include <asm/pgtable.h> + +#include "internal.h" #define _COMPONENT ACPI_BUS_COMPONENT -ACPI_MODULE_NAME("scan") -#define STRUCT_TO_INT(s) (*((int*)&s)) +ACPI_MODULE_NAME("scan"); extern struct acpi_device *acpi_root; #define ACPI_BUS_CLASS "system_bus" -#define ACPI_BUS_HID "ACPI_BUS" -#define ACPI_BUS_DRIVER_NAME "ACPI Bus Driver" +#define ACPI_BUS_HID "LNXSYBUS" #define ACPI_BUS_DEVICE_NAME "System Bus" -static LIST_HEAD(acpi_device_list); -DEFINE_SPINLOCK(acpi_device_lock); -LIST_HEAD(acpi_wakeup_device_list); +#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) -static int acpi_bus_trim(struct acpi_device *start, int rmdevice); +#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page) -static void acpi_device_release(struct kobject *kobj) -{ - struct acpi_device *dev = container_of(kobj, struct acpi_device, kobj); - if (dev->pnp.cid_list) - kfree(dev->pnp.cid_list); - kfree(dev); -} - -struct acpi_device_attribute { - struct attribute attr; - ssize_t(*show) (struct acpi_device *, char *); - ssize_t(*store) (struct acpi_device *, const char *, size_t); -}; +/* + * If set, devices will be hot-removed even if they cannot be put offline + * gracefully (from the kernel's standpoint). + */ +bool acpi_force_hot_remove; -typedef void acpi_device_sysfs_files(struct kobject *, - const struct attribute *); +static const char *dummy_hid = "device"; -static void setup_sys_fs_device_files(struct acpi_device *dev, - acpi_device_sysfs_files * func); +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); +static DEFINE_MUTEX(acpi_hp_context_lock); -#define create_sysfs_device_files(dev) \ - setup_sys_fs_device_files(dev, (acpi_device_sysfs_files *)&sysfs_create_file) -#define remove_sysfs_device_files(dev) \ - setup_sys_fs_device_files(dev, (acpi_device_sysfs_files *)&sysfs_remove_file) +struct acpi_device_bus_id{ + char bus_id[15]; + unsigned int instance_no; + struct list_head node; +}; -#define to_acpi_device(n) container_of(n, struct acpi_device, kobj) -#define to_handle_attr(n) container_of(n, struct acpi_device_attribute, attr); +void acpi_scan_lock_acquire(void) +{ + mutex_lock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_acquire); -static ssize_t acpi_device_attr_show(struct kobject *kobj, - struct attribute *attr, char *buf) +void acpi_scan_lock_release(void) { - struct acpi_device *device = to_acpi_device(kobj); - struct acpi_device_attribute *attribute = to_handle_attr(attr); - return attribute->show ? attribute->show(device, buf) : -EIO; + mutex_unlock(&acpi_scan_lock); } -static ssize_t acpi_device_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, - size_t len) +EXPORT_SYMBOL_GPL(acpi_scan_lock_release); + +void acpi_lock_hp_context(void) { - struct acpi_device *device = to_acpi_device(kobj); - struct acpi_device_attribute *attribute = to_handle_attr(attr); - return attribute->store ? attribute->store(device, buf, len) : -EIO; + mutex_lock(&acpi_hp_context_lock); } -static struct sysfs_ops acpi_device_sysfs_ops = { - .show = acpi_device_attr_show, - .store = acpi_device_attr_store, -}; +void acpi_unlock_hp_context(void) +{ + mutex_unlock(&acpi_hp_context_lock); +} -static struct kobj_type ktype_acpi_ns = { - .sysfs_ops = &acpi_device_sysfs_ops, - .release = acpi_device_release, -}; +void acpi_initialize_hp_context(struct acpi_device *adev, + struct acpi_hotplug_context *hp, + int (*notify)(struct acpi_device *, u32), + void (*uevent)(struct acpi_device *, u32)) +{ + acpi_lock_hp_context(); + acpi_set_hp_context(adev, hp, notify, uevent, NULL); + acpi_unlock_hp_context(); +} +EXPORT_SYMBOL_GPL(acpi_initialize_hp_context); -static int namespace_hotplug(struct kset *kset, struct kobject *kobj, - char **envp, int num_envp, char *buffer, - int buffer_size) +int acpi_scan_add_handler(struct acpi_scan_handler *handler) { - struct acpi_device *dev = to_acpi_device(kobj); - int i = 0; - int len = 0; + if (!handler) + return -EINVAL; - if (!dev->driver) - return 0; + list_add_tail(&handler->list_node, &acpi_scan_handlers_list); + return 0; +} - if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &len, - "PHYSDEVDRIVER=%s", dev->driver->name)) - return -ENOMEM; +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name) +{ + int error; - envp[i] = NULL; + error = acpi_scan_add_handler(handler); + if (error) + return error; + acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name); return 0; } -static struct kset_hotplug_ops namespace_hotplug_ops = { - .hotplug = &namespace_hotplug, -}; - -static struct kset acpi_namespace_kset = { - .kobj = { - .name = "namespace", - }, - .subsys = &acpi_subsys, - .ktype = &ktype_acpi_ns, - .hotplug_ops = &namespace_hotplug_ops, -}; - -static void acpi_device_register(struct acpi_device *device, - struct acpi_device *parent) +/* + * Creates hid/cid(s) string needed for modalias and uevent + * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: + * char *modalias: "acpi:IBM0001:ACPI0001" + * Return: 0: no _HID and no _CID + * -EINVAL: output error + * -ENOMEM: output is truncated +*/ +static int create_modalias(struct acpi_device *acpi_dev, char *modalias, + int size) { - /* - * Linkage - * ------- - * Link this device to its parent and siblings. - */ - INIT_LIST_HEAD(&device->children); - INIT_LIST_HEAD(&device->node); - INIT_LIST_HEAD(&device->g_list); - INIT_LIST_HEAD(&device->wakeup_list); + int len; + int count; + struct acpi_hardware_id *id; - spin_lock(&acpi_device_lock); - if (device->parent) { - list_add_tail(&device->node, &device->parent->children); - list_add_tail(&device->g_list, &device->parent->g_list); - } else - list_add_tail(&device->g_list, &acpi_device_list); - if (device->wakeup.flags.valid) - list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list); - spin_unlock(&acpi_device_lock); + if (list_empty(&acpi_dev->pnp.ids)) + return 0; + + len = snprintf(modalias, size, "acpi:"); + size -= len; + + list_for_each_entry(id, &acpi_dev->pnp.ids, list) { + count = snprintf(&modalias[len], size, "%s:", id->id); + if (count < 0) + return EINVAL; + if (count >= size) + return -ENOMEM; + len += count; + size -= count; + } - strlcpy(device->kobj.name, device->pnp.bus_id, KOBJ_NAME_LEN); - if (parent) - device->kobj.parent = &parent->kobj; - device->kobj.ktype = &ktype_acpi_ns; - device->kobj.kset = &acpi_namespace_kset; - kobject_register(&device->kobj); - create_sysfs_device_files(device); + modalias[len] = '\0'; + return len; } -static int acpi_device_unregister(struct acpi_device *device, int type) +/* + * Creates uevent modalias field for ACPI enumerated devices. + * Because the other buses does not support ACPI HIDs & CIDs. + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: + * "acpi:IBM0001:ACPI0001" + */ +int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) { - spin_lock(&acpi_device_lock); - if (device->parent) { - list_del(&device->node); - list_del(&device->g_list); - } else - list_del(&device->g_list); + struct acpi_device *acpi_dev; + int len; - list_del(&device->wakeup_list); + acpi_dev = ACPI_COMPANION(dev); + if (!acpi_dev) + return -ENODEV; - spin_unlock(&acpi_device_lock); + /* Fall back to bus specific way of modalias exporting */ + if (list_empty(&acpi_dev->pnp.ids)) + return -ENODEV; - acpi_detach_data(device->handle, acpi_bus_data_handler); - remove_sysfs_device_files(device); - kobject_unregister(&device->kobj); + if (add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], + sizeof(env->buf) - env->buflen); + if (len <= 0) + return len; + env->buflen += len; return 0; } +EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); -void acpi_bus_data_handler(acpi_handle handle, u32 function, void *context) +/* + * Creates modalias sysfs attribute for ACPI enumerated devices. + * Because the other buses does not support ACPI HIDs & CIDs. + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: + * "acpi:IBM0001:ACPI0001" + */ +int acpi_device_modalias(struct device *dev, char *buf, int size) { - ACPI_FUNCTION_TRACE("acpi_bus_data_handler"); - - /* TBD */ + struct acpi_device *acpi_dev; + int len; + + acpi_dev = ACPI_COMPANION(dev); + if (!acpi_dev) + return -ENODEV; + + /* Fall back to bus specific way of modalias exporting */ + if (list_empty(&acpi_dev->pnp.ids)) + return -ENODEV; + + len = create_modalias(acpi_dev, buf, size -1); + if (len <= 0) + return len; + buf[len++] = '\n'; + return len; +} +EXPORT_SYMBOL_GPL(acpi_device_modalias); - return_VOID; +static ssize_t +acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + int len; + + len = create_modalias(acpi_dev, buf, 1024); + if (len <= 0) + return len; + buf[len++] = '\n'; + return len; } +static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static int acpi_bus_get_power_flags(struct acpi_device *device) +bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) { - acpi_status status = 0; - acpi_handle handle = NULL; - u32 i = 0; + struct acpi_device_physical_node *pn; + bool offline = true; - ACPI_FUNCTION_TRACE("acpi_bus_get_power_flags"); + mutex_lock(&adev->physical_node_lock); - /* - * Power Management Flags - */ - status = acpi_get_handle(device->handle, "_PSC", &handle); - if (ACPI_SUCCESS(status)) - device->power.flags.explicit_get = 1; - status = acpi_get_handle(device->handle, "_IRC", &handle); - if (ACPI_SUCCESS(status)) - device->power.flags.inrush_current = 1; + list_for_each_entry(pn, &adev->physical_node_list, node) + if (device_supports_offline(pn->dev) && !pn->dev->offline) { + if (uevent) + kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); - /* - * Enumerate supported power management states - */ - for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3; 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) { - device->power.flags.power_resources = 1; - ps->flags.valid = 1; + offline = false; + break; } - /* 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; - ps->flags.valid = 1; - } + mutex_unlock(&adev->physical_node_lock); + return offline; +} - /* State is valid if we have some power control */ - if (ps->resources.count || ps->flags.explicit_set) - ps->flags.valid = 1; +static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data, + void **ret_p) +{ + struct acpi_device *device = NULL; + struct acpi_device_physical_node *pn; + bool second_pass = (bool)data; + acpi_status status = AE_OK; - ps->power = -1; /* Unknown - driver assigned */ - ps->latency = -1; /* Unknown - driver assigned */ + if (acpi_bus_get_device(handle, &device)) + return AE_OK; + + if (device->handler && !device->handler->hotplug.enabled) { + *ret_p = &device->dev; + return AE_SUPPORT; } - /* 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; - device->power.states[ACPI_STATE_D3].flags.valid = 1; - device->power.states[ACPI_STATE_D3].power = 0; + mutex_lock(&device->physical_node_lock); + + list_for_each_entry(pn, &device->physical_node_list, node) { + int ret; + + if (second_pass) { + /* Skip devices offlined by the first pass. */ + if (pn->put_online) + continue; + } else { + pn->put_online = false; + } + ret = device_offline(pn->dev); + if (acpi_force_hot_remove) + continue; - /* TBD: System wake support and resource requirements. */ + if (ret >= 0) { + pn->put_online = !ret; + } else { + *ret_p = pn->dev; + if (second_pass) { + status = AE_ERROR; + break; + } + } + } - device->power.state = ACPI_STATE_UNKNOWN; + mutex_unlock(&device->physical_node_lock); - return_VALUE(0); + return status; } -int acpi_match_ids(struct acpi_device *device, char *ids) +static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data, + void **ret_p) { - int error = 0; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_device *device = NULL; + struct acpi_device_physical_node *pn; - if (device->flags.hardware_id) - if (strstr(ids, device->pnp.hardware_id)) - goto Done; + if (acpi_bus_get_device(handle, &device)) + return AE_OK; - if (device->flags.compatible_ids) { - struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; - int i; + mutex_lock(&device->physical_node_lock); - /* compare multiple _CID entries against driver ids */ - for (i = 0; i < cid_list->count; i++) { - if (strstr(ids, cid_list->id[i].value)) - goto Done; + list_for_each_entry(pn, &device->physical_node_list, node) + if (pn->put_online) { + device_online(pn->dev); + pn->put_online = false; } - } - error = -ENOENT; - Done: - if (buffer.pointer) - acpi_os_free(buffer.pointer); - return error; + mutex_unlock(&device->physical_node_lock); + + return AE_OK; } -static acpi_status -acpi_bus_extract_wakeup_device_power_package(struct acpi_device *device, - union acpi_object *package) +static int acpi_scan_try_to_offline(struct acpi_device *device) { - int i = 0; - union acpi_object *element = NULL; + acpi_handle handle = device->handle; + struct device *errdev = NULL; + acpi_status status; - if (!device || !package || (package->package.count < 2)) - return AE_BAD_PARAMETER; + /* + * Carry out two passes here and ignore errors in the first pass, + * because if the devices in question are memory blocks and + * CONFIG_MEMCG is set, one of the blocks may hold data structures + * that the other blocks depend on, but it is not known in advance which + * block holds them. + * + * If the first pass is successful, the second one isn't needed, though. + */ + status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + NULL, acpi_bus_offline, (void *)false, + (void **)&errdev); + if (status == AE_SUPPORT) { + dev_warn(errdev, "Offline disabled.\n"); + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_online, NULL, NULL, NULL); + return -EPERM; + } + acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev); + if (errdev) { + errdev = NULL; + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + NULL, acpi_bus_offline, (void *)true, + (void **)&errdev); + if (!errdev || acpi_force_hot_remove) + acpi_bus_offline(handle, 0, (void *)true, + (void **)&errdev); + + if (errdev && !acpi_force_hot_remove) { + dev_warn(errdev, "Offline failed.\n"); + acpi_bus_online(handle, 0, NULL, NULL); + acpi_walk_namespace(ACPI_TYPE_ANY, handle, + ACPI_UINT32_MAX, acpi_bus_online, + NULL, NULL, NULL); + return -EBUSY; + } + } + return 0; +} - element = &(package->package.elements[0]); - if (!element) - return AE_BAD_PARAMETER; - 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)) - return AE_BAD_DATA; - device->wakeup.gpe_device = - element->package.elements[0].reference.handle; - device->wakeup.gpe_number = - (u32) element->package.elements[1].integer.value; - } else if (element->type == ACPI_TYPE_INTEGER) { - device->wakeup.gpe_number = element->integer.value; - } else - return AE_BAD_DATA; +static int acpi_scan_hot_remove(struct acpi_device *device) +{ + acpi_handle handle = device->handle; + unsigned long long sta; + acpi_status status; - element = &(package->package.elements[1]); - if (element->type != ACPI_TYPE_INTEGER) { - return AE_BAD_DATA; + if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) { + if (!acpi_scan_is_offline(device, true)) + return -EBUSY; + } else { + int error = acpi_scan_try_to_offline(device); + if (error) + return error; } - device->wakeup.sleep_state = element->integer.value; - if ((package->package.count - 2) > ACPI_MAX_HANDLES) { - return AE_NO_MEMORY; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Hot-removing device %s...\n", dev_name(&device->dev))); + + acpi_bus_trim(device); + + acpi_evaluate_lck(handle, 0); + /* + * TBD: _EJD support. + */ + status = acpi_evaluate_ej0(handle); + if (status == AE_NOT_FOUND) + return -ENODEV; + else if (ACPI_FAILURE(status)) + return -EIO; + + /* + * Verify if eject was indeed successful. If not, log an error + * message. No need to call _OST since _EJ0 call was made OK. + */ + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, + "Status check after eject failed (0x%x)\n", status); + } else if (sta & ACPI_STA_DEVICE_ENABLED) { + acpi_handle_warn(handle, + "Eject incomplete - status 0x%llx\n", sta); } - device->wakeup.resources.count = package->package.count - 2; - for (i = 0; i < device->wakeup.resources.count; i++) { - element = &(package->package.elements[i + 2]); - if (element->type != ACPI_TYPE_ANY) { - return AE_BAD_DATA; - } - device->wakeup.resources.handles[i] = element->reference.handle; + return 0; +} + +static int acpi_scan_device_not_present(struct acpi_device *adev) +{ + if (!acpi_device_enumerated(adev)) { + dev_warn(&adev->dev, "Still not present\n"); + return -EALREADY; } + acpi_bus_trim(adev); + return 0; +} - return AE_OK; +static int acpi_scan_device_check(struct acpi_device *adev) +{ + int error; + + acpi_bus_get_status(adev); + if (adev->status.present || adev->status.functional) { + /* + * This function is only called for device objects for which + * matching scan handlers exist. The only situation in which + * the scan handler is not attached to this device object yet + * is when the device has just appeared (either it wasn't + * present at all before or it was removed and then added + * again). + */ + if (adev->handler) { + dev_warn(&adev->dev, "Already enumerated\n"); + return -EALREADY; + } + error = acpi_bus_scan(adev->handle); + if (error) { + dev_warn(&adev->dev, "Namespace scan failure\n"); + return error; + } + if (!adev->handler) { + dev_warn(&adev->dev, "Enumeration failure\n"); + error = -ENODEV; + } + } else { + error = acpi_scan_device_not_present(adev); + } + return error; } -static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) +static int acpi_scan_bus_check(struct acpi_device *adev) { - acpi_status status = 0; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *package = NULL; + struct acpi_scan_handler *handler = adev->handler; + struct acpi_device *child; + int error; - ACPI_FUNCTION_TRACE("acpi_bus_get_wakeup_flags"); + acpi_bus_get_status(adev); + if (!(adev->status.present || adev->status.functional)) { + acpi_scan_device_not_present(adev); + return 0; + } + if (handler && handler->hotplug.scan_dependent) + return handler->hotplug.scan_dependent(adev); - /* _PRW */ - status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRW\n")); - goto end; + error = acpi_bus_scan(adev->handle); + if (error) { + dev_warn(&adev->dev, "Namespace scan failure\n"); + return error; } + list_for_each_entry(child, &adev->children, node) { + error = acpi_scan_bus_check(child); + if (error) + return error; + } + return 0; +} - package = (union acpi_object *)buffer.pointer; - status = acpi_bus_extract_wakeup_device_power_package(device, package); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Error extracting _PRW package\n")); - goto end; +static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type) +{ + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + return acpi_scan_bus_check(adev); + case ACPI_NOTIFY_DEVICE_CHECK: + return acpi_scan_device_check(adev); + case ACPI_NOTIFY_EJECT_REQUEST: + case ACPI_OST_EC_OSPM_EJECT: + if (adev->handler && !adev->handler->hotplug.enabled) { + dev_info(&adev->dev, "Eject disabled\n"); + return -EPERM; + } + acpi_evaluate_ost(adev->handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + return acpi_scan_hot_remove(adev); } + return -EINVAL; +} - acpi_os_free(buffer.pointer); +void acpi_device_hotplug(struct acpi_device *adev, u32 src) +{ + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + int error = -ENODEV; - device->wakeup.flags.valid = 1; - /* Power button, Lid switch always enable wakeup */ - if (!acpi_match_ids(device, "PNP0C0D,PNP0C0C,PNP0C0E")) - device->wakeup.flags.run_wake = 1; + lock_device_hotplug(); + mutex_lock(&acpi_scan_lock); - end: - if (ACPI_FAILURE(status)) - device->flags.wake_capable = 0; - return_VALUE(0); + /* + * The device object's ACPI handle cannot become invalid as long as we + * are holding acpi_scan_lock, but it might have become invalid before + * that lock was acquired. + */ + if (adev->handle == INVALID_ACPI_HANDLE) + goto err_out; + + if (adev->flags.is_dock_station) { + error = dock_notify(adev, src); + } else if (adev->flags.hotplug_notify) { + error = acpi_generic_hotplug_event(adev, src); + if (error == -EPERM) { + ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; + goto err_out; + } + } else { + int (*notify)(struct acpi_device *, u32); + + acpi_lock_hp_context(); + notify = adev->hp ? adev->hp->notify : NULL; + acpi_unlock_hp_context(); + /* + * There may be additional notify handlers for device objects + * without the .event() callback, so ignore them here. + */ + if (notify) + error = notify(adev, src); + else + goto out; + } + if (!error) + ost_code = ACPI_OST_SC_SUCCESS; + + err_out: + acpi_evaluate_ost(adev->handle, src, ost_code, NULL); + + out: + acpi_bus_put_acpi_device(adev); + mutex_unlock(&acpi_scan_lock); + unlock_device_hotplug(); } -/* -------------------------------------------------------------------------- - ACPI hotplug sysfs device file support - -------------------------------------------------------------------------- */ -static ssize_t acpi_eject_store(struct acpi_device *device, - const char *buf, size_t count); +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; -#define ACPI_DEVICE_ATTR(_name,_mode,_show,_store) \ -static struct acpi_device_attribute acpi_device_attr_##_name = \ - __ATTR(_name, _mode, _show, _store) + ret = acpi_device_get_power(adev, &state); + if (ret) + return ret; -ACPI_DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); + return sprintf(buf, "%s\n", acpi_power_state_string(state)); +} -/** - * setup_sys_fs_device_files - sets up the device files under device namespace - * @dev: acpi_device object - * @func: function pointer to create or destroy the device file - */ -static void -setup_sys_fs_device_files(struct acpi_device *dev, - acpi_device_sysfs_files * func) +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) +{ + struct acpi_device *acpi_device = to_acpi_device(d); + acpi_object_type not_used; acpi_status status; - acpi_handle temp = NULL; - /* - * If device has _EJ0, 'eject' file is created that is used to trigger - * hot-removal function from userland. - */ - status = acpi_get_handle(dev->handle, "_EJ0", &temp); + if (!count || buf[0] != '1') + return -EINVAL; + + if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) + && !acpi_device->driver) + return -ENODEV; + + status = acpi_get_type(acpi_device->handle, ¬_used); + if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) + return -ENODEV; + + get_device(&acpi_device->dev); + status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); if (ACPI_SUCCESS(status)) - (*(func)) (&dev->kobj, &acpi_device_attr_eject.attr); + return count; + + put_device(&acpi_device->dev); + acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; +} + +static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); + +static ssize_t +acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + + return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev)); } +static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL); -static int acpi_eject_operation(acpi_handle handle, int lockable) +static ssize_t acpi_device_uid_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct acpi_object_list arg_list; - union acpi_object arg; - acpi_status status = AE_OK; + struct acpi_device *acpi_dev = to_acpi_device(dev); - /* - * TBD: evaluate _PS3? - */ + return sprintf(buf, "%s\n", acpi_dev->pnp.unique_id); +} +static DEVICE_ATTR(uid, 0444, acpi_device_uid_show, NULL); - if (lockable) { - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = 0; - acpi_evaluate_object(handle, "_LCK", &arg_list, NULL); - } +static ssize_t acpi_device_adr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + + return sprintf(buf, "0x%08x\n", + (unsigned int)(acpi_dev->pnp.bus_address)); +} +static DEVICE_ATTR(adr, 0444, acpi_device_adr_show, NULL); + +static ssize_t +acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; + int result; + + result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path); + if (result) + goto end; + + result = sprintf(buf, "%s\n", (char*)path.pointer); + kfree(path.pointer); +end: + return result; +} +static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = 1; +/* sysfs file that shows description text from the ACPI _STR method */ +static ssize_t description_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + int result; + + if (acpi_dev->pnp.str_obj == NULL) + return 0; /* - * TBD: _EJD support. + * The _STR object contains a Unicode identifier for a device. + * We need to convert to utf-8 so it can be displayed. */ + result = utf16s_to_utf8s( + (wchar_t *)acpi_dev->pnp.str_obj->buffer.pointer, + acpi_dev->pnp.str_obj->buffer.length, + UTF16_LITTLE_ENDIAN, buf, + PAGE_SIZE); - status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); - if (ACPI_FAILURE(status)) { - return (-ENODEV); - } + buf[result++] = '\n'; - return (0); + return result; } +static DEVICE_ATTR(description, 0444, description_show, NULL); static ssize_t -acpi_eject_store(struct acpi_device *device, const char *buf, size_t count) +acpi_device_sun_show(struct device *dev, struct device_attribute *attr, + char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + + return sprintf(buf, "%lu\n", acpi_dev->pnp.sun); +} +static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL); + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + acpi_status status; + unsigned long long sta; + + status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return sprintf(buf, "%llu\n", sta); +} +static DEVICE_ATTR_RO(status); + +static int acpi_device_setup_files(struct acpi_device *dev) { - int result; - int ret = count; - int islockable; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; acpi_status status; - acpi_handle handle; - acpi_object_type type = 0; + unsigned long long sun; + int result = 0; - if ((!count) || (buf[0] != '1')) { - return -EINVAL; + /* + * Devices gotten from FADT don't have a "path" attribute + */ + if (dev->handle) { + result = device_create_file(&dev->dev, &dev_attr_path); + if (result) + goto end; } -#ifndef FORCE_EJECT - if (device->driver == NULL) { - ret = -ENODEV; - goto err; + + if (!list_empty(&dev->pnp.ids)) { + result = device_create_file(&dev->dev, &dev_attr_hid); + if (result) + goto end; + + result = device_create_file(&dev->dev, &dev_attr_modalias); + if (result) + goto end; } -#endif - status = acpi_get_type(device->handle, &type); - if (ACPI_FAILURE(status) || (!device->flags.ejectable)) { - ret = -ENODEV; - goto err; + + /* + * If device has _STR, 'description' file is created + */ + if (acpi_has_method(dev->handle, "_STR")) { + status = acpi_evaluate_object(dev->handle, "_STR", + NULL, &buffer); + if (ACPI_FAILURE(status)) + buffer.pointer = NULL; + dev->pnp.str_obj = buffer.pointer; + result = device_create_file(&dev->dev, &dev_attr_description); + if (result) + goto end; } - islockable = device->flags.lockable; - handle = device->handle; + if (dev->pnp.type.bus_address) + result = device_create_file(&dev->dev, &dev_attr_adr); + if (dev->pnp.unique_id) + result = device_create_file(&dev->dev, &dev_attr_uid); - if (type == ACPI_TYPE_PROCESSOR) - result = acpi_bus_trim(device, 0); - else - result = acpi_bus_trim(device, 1); + status = acpi_evaluate_integer(dev->handle, "_SUN", NULL, &sun); + if (ACPI_SUCCESS(status)) { + dev->pnp.sun = (unsigned long)sun; + result = device_create_file(&dev->dev, &dev_attr_sun); + if (result) + goto end; + } else { + dev->pnp.sun = (unsigned long)-1; + } - if (!result) - result = acpi_eject_operation(handle, islockable); + if (acpi_has_method(dev->handle, "_STA")) { + result = device_create_file(&dev->dev, &dev_attr_status); + if (result) + goto end; + } - if (result) { - ret = -EBUSY; + /* + * If device has _EJ0, 'eject' file is created that is used to trigger + * hot-removal function from userland. + */ + if (acpi_has_method(dev->handle, "_EJ0")) { + result = device_create_file(&dev->dev, &dev_attr_eject); + if (result) + return result; } - err: - return ret; + + 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; } +static void acpi_device_remove_files(struct acpi_device *dev) +{ + 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 + */ + if (acpi_has_method(dev->handle, "_STR")) { + kfree(dev->pnp.str_obj); + device_remove_file(&dev->dev, &dev_attr_description); + } + /* + * If device has _EJ0, remove 'eject' file. + */ + if (acpi_has_method(dev->handle, "_EJ0")) + device_remove_file(&dev->dev, &dev_attr_eject); + + if (acpi_has_method(dev->handle, "_SUN")) + device_remove_file(&dev->dev, &dev_attr_sun); + + if (dev->pnp.unique_id) + device_remove_file(&dev->dev, &dev_attr_uid); + if (dev->pnp.type.bus_address) + device_remove_file(&dev->dev, &dev_attr_adr); + device_remove_file(&dev->dev, &dev_attr_modalias); + device_remove_file(&dev->dev, &dev_attr_hid); + if (acpi_has_method(dev->handle, "_STA")) + device_remove_file(&dev->dev, &dev_attr_status); + if (dev->handle) + device_remove_file(&dev->dev, &dev_attr_path); +} /* -------------------------------------------------------------------------- - Performance Management + ACPI Bus operations -------------------------------------------------------------------------- */ -static int acpi_bus_get_perf_flags(struct acpi_device *device) +static const struct acpi_device_id *__acpi_match_device( + struct acpi_device *device, const struct acpi_device_id *ids) { - device->performance.state = ACPI_STATE_UNKNOWN; - return 0; -} + const struct acpi_device_id *id; + struct acpi_hardware_id *hwid; -/* -------------------------------------------------------------------------- - Driver Management - -------------------------------------------------------------------------- */ + /* + * If the device is not present, it is unnecessary to load device + * driver for it. + */ + if (!device->status.present) + return NULL; -static LIST_HEAD(acpi_bus_drivers); -static DECLARE_MUTEX(acpi_bus_drivers_lock); + for (id = ids; id->id[0]; id++) + list_for_each_entry(hwid, &device->pnp.ids, list) + if (!strcmp((char *) id->id, hwid->id)) + return id; + + return NULL; +} /** - * acpi_bus_match - * -------------- - * Checks the device's hardware (_HID) or compatible (_CID) ids to see if it - * matches the specified driver's criteria. + * acpi_match_device - Match a struct device against a given list of ACPI IDs + * @ids: Array of struct acpi_device_id object to match against. + * @dev: The device structure to match. + * + * Check if @dev has a valid ACPI handle and if there is a struct acpi_device + * object for that handle and use that object to match against a given list of + * device IDs. + * + * Return a pointer to the first matching ID on success or %NULL on failure. */ -static int -acpi_bus_match(struct acpi_device *device, struct acpi_driver *driver) +const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, + const struct device *dev) { - if (driver && driver->ops.match) - return driver->ops.match(device, driver); - return acpi_match_ids(device, driver->ids); + struct acpi_device *adev; + acpi_handle handle = ACPI_HANDLE(dev); + + if (!ids || !handle || acpi_bus_get_device(handle, &adev)) + return NULL; + + return __acpi_match_device(adev, ids); } +EXPORT_SYMBOL_GPL(acpi_match_device); -/** - * acpi_bus_driver_init - * -------------------- - * Used to initialize a device via its device driver. Called whenever a - * driver is bound to a device. Invokes the driver's add() and start() ops. - */ -static int -acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver) +int acpi_match_device_ids(struct acpi_device *device, + const struct acpi_device_id *ids) { - int result = 0; + return __acpi_match_device(device, ids) ? 0 : -ENOENT; +} +EXPORT_SYMBOL(acpi_match_device_ids); - ACPI_FUNCTION_TRACE("acpi_bus_driver_init"); +static void acpi_free_power_resources_lists(struct acpi_device *device) +{ + int i; - if (!device || !driver) - return_VALUE(-EINVAL); + if (device->wakeup.flags.valid) + acpi_power_resources_list_free(&device->wakeup.resources); - if (!driver->ops.add) - return_VALUE(-ENOSYS); + if (!device->flags.power_manageable) + return; - result = driver->ops.add(device); - if (result) { - device->driver = NULL; - acpi_driver_data(device) = NULL; - return_VALUE(result); + 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); } +} - device->driver = driver; +static void acpi_device_release(struct device *dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); - /* - * TBD - Configuration Management: Assign resources to device based - * upon possible configuration and currently allocated resources. - */ + acpi_free_pnp_ids(&acpi_dev->pnp); + acpi_free_power_resources_lists(acpi_dev); + kfree(acpi_dev); +} - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Driver successfully bound to device\n")); - return_VALUE(0); +static int acpi_bus_match(struct device *dev, struct device_driver *drv) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = to_acpi_driver(drv); + + return acpi_dev->flags.match_driver + && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); } -static int acpi_start_single_object(struct acpi_device *device) +static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - int result = 0; - struct acpi_driver *driver; + struct acpi_device *acpi_dev = to_acpi_device(dev); + int len; - ACPI_FUNCTION_TRACE("acpi_start_single_object"); + if (list_empty(&acpi_dev->pnp.ids)) + return 0; - if (!(driver = device->driver)) - return_VALUE(0); + if (add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], + sizeof(env->buf) - env->buflen); + if (len <= 0) + return len; + env->buflen += len; + return 0; +} + +static void acpi_device_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_device *device = data; - if (driver->ops.start) { - result = driver->ops.start(device); - if (result && driver->ops.remove) - driver->ops.remove(device, ACPI_BUS_REMOVAL_NORMAL); + device->driver->ops.notify(device, event); +} + +static acpi_status acpi_device_notify_fixed(void *data) +{ + struct acpi_device *device = data; + + /* Fixed hardware devices have no handles */ + acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device); + return AE_OK; +} + +static int acpi_device_install_notify_handler(struct acpi_device *device) +{ + acpi_status status; + + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) + status = + acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_device_notify_fixed, + device); + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) + status = + acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_device_notify_fixed, + device); + else + status = acpi_install_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, + acpi_device_notify, + device); + + if (ACPI_FAILURE(status)) + return -EINVAL; + return 0; +} + +static void acpi_device_remove_notify_handler(struct acpi_device *device) +{ + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) + acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_device_notify_fixed); + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) + acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_device_notify_fixed); + else + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_device_notify); +} + +static int acpi_device_probe(struct device *dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); + int ret; + + if (acpi_dev->handler) + return -EINVAL; + + if (!acpi_drv->ops.add) + return -ENOSYS; + + ret = acpi_drv->ops.add(acpi_dev); + if (ret) + return ret; + + acpi_dev->driver = acpi_drv; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Driver [%s] successfully bound to device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + + if (acpi_drv->ops.notify) { + ret = acpi_device_install_notify_handler(acpi_dev); + if (ret) { + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev); + + acpi_dev->driver = NULL; + acpi_dev->driver_data = NULL; + return ret; + } } - return_VALUE(result); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found driver [%s] for device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + get_device(dev); + return 0; } -static int acpi_driver_attach(struct acpi_driver *drv) +static int acpi_device_remove(struct device * dev) { - struct list_head *node, *next; - int count = 0; + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = acpi_dev->driver; + + if (acpi_drv) { + 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->driver = NULL; + acpi_dev->driver_data = NULL; - ACPI_FUNCTION_TRACE("acpi_driver_attach"); + put_device(dev); + return 0; +} - spin_lock(&acpi_device_lock); - list_for_each_safe(node, next, &acpi_device_list) { - struct acpi_device *dev = - container_of(node, struct acpi_device, g_list); +struct bus_type acpi_bus_type = { + .name = "acpi", + .match = acpi_bus_match, + .probe = acpi_device_probe, + .remove = acpi_device_remove, + .uevent = acpi_device_uevent, +}; - if (dev->driver || !dev->status.present) - continue; - spin_unlock(&acpi_device_lock); - - if (!acpi_bus_match(dev, drv)) { - if (!acpi_bus_driver_init(dev, drv)) { - acpi_start_single_object(dev); - atomic_inc(&drv->references); - count++; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found driver [%s] for device [%s]\n", - drv->name, dev->pnp.bus_id)); - } +static void acpi_device_del(struct acpi_device *device) +{ + mutex_lock(&acpi_device_lock); + if (device->parent) + list_del(&device->node); + + list_del(&device->wakeup_list); + mutex_unlock(&acpi_device_lock); + + acpi_power_add_remove_device(device, false); + acpi_device_remove_files(device); + if (device->remove) + device->remove(device); + + device_del(&device->dev); +} + +static LIST_HEAD(acpi_device_del_list); +static DEFINE_MUTEX(acpi_device_del_lock); + +static void acpi_device_del_work_fn(struct work_struct *work_not_used) +{ + for (;;) { + struct acpi_device *adev; + + mutex_lock(&acpi_device_del_lock); + + if (list_empty(&acpi_device_del_list)) { + mutex_unlock(&acpi_device_del_lock); + break; } - spin_lock(&acpi_device_lock); + adev = list_first_entry(&acpi_device_del_list, + struct acpi_device, del_list); + list_del(&adev->del_list); + + mutex_unlock(&acpi_device_del_lock); + + acpi_device_del(adev); + /* + * Drop references to all power resources that might have been + * used by the device. + */ + acpi_power_transition(adev, ACPI_STATE_D3_COLD); + put_device(&adev->dev); + } +} + +/** + * acpi_scan_drop_device - Drop an ACPI device object. + * @handle: Handle of an ACPI namespace node, not used. + * @context: Address of the ACPI device object to drop. + * + * This is invoked by acpi_ns_delete_node() during the removal of the ACPI + * namespace node the device object pointed to by @context is attached to. + * + * The unregistration is carried out asynchronously to avoid running + * acpi_device_del() under the ACPICA's namespace mutex and the list is used to + * ensure the correct ordering (the device objects must be unregistered in the + * same order in which the corresponding namespace nodes are deleted). + */ +static void acpi_scan_drop_device(acpi_handle handle, void *context) +{ + static DECLARE_WORK(work, acpi_device_del_work_fn); + struct acpi_device *adev = context; + + mutex_lock(&acpi_device_del_lock); + + /* + * Use the ACPI hotplug workqueue which is ordered, so this work item + * won't run after any hotplug work items submitted subsequently. That + * prevents attempts to register device objects identical to those being + * deleted from happening concurrently (such attempts result from + * hotplug events handled via the ACPI hotplug workqueue). It also will + * run after all of the work items submitted previosuly, which helps + * those work items to ensure that they are not accessing stale device + * objects. + */ + if (list_empty(&acpi_device_del_list)) + acpi_queue_hotplug_work(&work); + + list_add_tail(&adev->del_list, &acpi_device_del_list); + /* Make acpi_ns_validate_handle() return NULL for this handle. */ + adev->handle = INVALID_ACPI_HANDLE; + + mutex_unlock(&acpi_device_del_lock); +} + +static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device, + void (*callback)(void *)) +{ + acpi_status status; + + if (!device) + return -EINVAL; + + status = acpi_get_data_full(handle, acpi_scan_drop_device, + (void **)device, callback); + if (ACPI_FAILURE(status) || !*device) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", + handle)); + return -ENODEV; } - spin_unlock(&acpi_device_lock); - return_VALUE(count); + return 0; } -static int acpi_driver_detach(struct acpi_driver *drv) +int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) { - struct list_head *node, *next; + return acpi_get_device_data(handle, device, NULL); +} +EXPORT_SYMBOL(acpi_bus_get_device); + +static void get_acpi_device(void *dev) +{ + if (dev) + get_device(&((struct acpi_device *)dev)->dev); +} - ACPI_FUNCTION_TRACE("acpi_driver_detach"); +struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle) +{ + struct acpi_device *adev = NULL; + + acpi_get_device_data(handle, &adev, get_acpi_device); + return adev; +} - spin_lock(&acpi_device_lock); - list_for_each_safe(node, next, &acpi_device_list) { - struct acpi_device *dev = - container_of(node, struct acpi_device, g_list); +void acpi_bus_put_acpi_device(struct acpi_device *adev) +{ + put_device(&adev->dev); +} + +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_scan_drop_device, + device); + if (ACPI_FAILURE(status)) { + acpi_handle_err(device->handle, + "Unable to attach device data\n"); + return -ENODEV; + } + } + + /* + * Linkage + * ------- + * Link this device to its parent and siblings. + */ + INIT_LIST_HEAD(&device->children); + INIT_LIST_HEAD(&device->node); + INIT_LIST_HEAD(&device->wakeup_list); + INIT_LIST_HEAD(&device->physical_node_list); + INIT_LIST_HEAD(&device->del_list); + mutex_init(&device->physical_node_lock); + + new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); + if (!new_bus_id) { + pr_err(PREFIX "Memory allocation error\n"); + result = -ENOMEM; + goto err_detach; + } - if (dev->driver == drv) { - spin_unlock(&acpi_device_lock); - if (drv->ops.remove) - drv->ops.remove(dev, ACPI_BUS_REMOVAL_NORMAL); - spin_lock(&acpi_device_lock); - dev->driver = NULL; - dev->driver_data = NULL; - atomic_dec(&drv->references); + mutex_lock(&acpi_device_lock); + /* + * Find suitable bus_id and instance number in acpi_bus_id_list + * If failed, create one and link it into acpi_bus_id_list + */ + list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) { + if (!strcmp(acpi_device_bus_id->bus_id, + acpi_device_hid(device))) { + acpi_device_bus_id->instance_no++; + found = 1; + kfree(new_bus_id); + break; } } - spin_unlock(&acpi_device_lock); - return_VALUE(0); + if (!found) { + acpi_device_bus_id = new_bus_id; + strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device)); + acpi_device_bus_id->instance_no = 0; + list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); + } + dev_set_name(&device->dev, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); + + if (device->parent) + list_add_tail(&device->node, &device->parent->children); + + if (device->wakeup.flags.valid) + list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list); + mutex_unlock(&acpi_device_lock); + + if (device->parent) + device->dev.parent = &device->parent->dev; + device->dev.bus = &acpi_bus_type; + device->dev.release = release; + result = device_add(&device->dev); + if (result) { + dev_err(&device->dev, "Error registering device\n"); + goto err; + } + + result = acpi_device_setup_files(device); + if (result) + printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", + dev_name(&device->dev)); + + return 0; + + 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_scan_drop_device); + return result; } +/* -------------------------------------------------------------------------- + Driver Management + -------------------------------------------------------------------------- */ /** - * acpi_bus_register_driver - * ------------------------ + * acpi_bus_register_driver - register a driver with the ACPI bus + * @driver: driver being registered + * * Registers a driver with the ACPI bus. Searches the namespace for all - * devices that match the driver's criteria and binds. Returns the - * number of devices that were claimed by the driver, or a negative - * error status for failure. + * devices that match the driver's criteria and binds. Returns zero for + * success or a negative error status for failure. */ int acpi_bus_register_driver(struct acpi_driver *driver) { - int count; - - ACPI_FUNCTION_TRACE("acpi_bus_register_driver"); + int ret; if (acpi_disabled) - return_VALUE(-ENODEV); + return -ENODEV; + driver->drv.name = driver->name; + driver->drv.bus = &acpi_bus_type; + driver->drv.owner = driver->owner; - if (!driver) - return_VALUE(-EINVAL); - - spin_lock(&acpi_device_lock); - list_add_tail(&driver->node, &acpi_bus_drivers); - spin_unlock(&acpi_device_lock); - count = acpi_driver_attach(driver); - - return_VALUE(count); + ret = driver_register(&driver->drv); + return ret; } EXPORT_SYMBOL(acpi_bus_register_driver); /** - * acpi_bus_unregister_driver - * -------------------------- + * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus + * @driver: driver to unregister + * * Unregisters a driver with the ACPI bus. Searches the namespace for all * devices that match the driver's criteria and unbinds. */ -int acpi_bus_unregister_driver(struct acpi_driver *driver) +void acpi_bus_unregister_driver(struct acpi_driver *driver) { - int error = 0; + driver_unregister(&driver->drv); +} - ACPI_FUNCTION_TRACE("acpi_bus_unregister_driver"); +EXPORT_SYMBOL(acpi_bus_unregister_driver); + +/* -------------------------------------------------------------------------- + Device Enumeration + -------------------------------------------------------------------------- */ +static struct acpi_device *acpi_bus_get_parent(acpi_handle handle) +{ + struct acpi_device *device = NULL; + acpi_status status; - if (driver) { - acpi_driver_detach(driver); + /* + * 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) + return acpi_root; - if (!atomic_read(&driver->references)) { - spin_lock(&acpi_device_lock); - list_del_init(&driver->node); - spin_unlock(&acpi_device_lock); - } - } else - error = -EINVAL; - return_VALUE(error); + do { + status = acpi_get_parent(handle, &handle); + if (ACPI_FAILURE(status)) + return status == AE_NULL_ENTRY ? NULL : acpi_root; + } while (acpi_bus_get_device(handle, &device)); + return device; } -EXPORT_SYMBOL(acpi_bus_unregister_driver); +acpi_status +acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) +{ + acpi_status status; + acpi_handle tmp; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; -/** - * acpi_bus_find_driver - * -------------------- - * Parses the list of registered drivers looking for a driver applicable for - * the specified device. - */ -static int acpi_bus_find_driver(struct acpi_device *device) + status = acpi_get_handle(handle, "_EJD", &tmp); + if (ACPI_FAILURE(status)) + return status; + + status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer); + if (ACPI_SUCCESS(status)) { + obj = buffer.pointer; + status = acpi_get_handle(ACPI_ROOT_OBJECT, obj->string.pointer, + ejd); + kfree(buffer.pointer); + } + return status; +} +EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); + +static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, + struct acpi_device_wakeup *wakeup) { - int result = 0; - struct list_head *node, *next; + 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 -EINVAL; - ACPI_FUNCTION_TRACE("acpi_bus_find_driver"); + INIT_LIST_HEAD(&wakeup->resources); - spin_lock(&acpi_device_lock); - list_for_each_safe(node, next, &acpi_bus_drivers) { - struct acpi_driver *driver = - container_of(node, struct acpi_driver, node); + /* _PRW */ + status = acpi_evaluate_object(handle, "_PRW", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRW")); + return err; + } - atomic_inc(&driver->references); - spin_unlock(&acpi_device_lock); - if (!acpi_bus_match(device, driver)) { - result = acpi_bus_driver_init(device, driver); - if (!result) - goto Done; + package = (union acpi_object *)buffer.pointer; + + if (!package || package->package.count < 2) + goto out; + + element = &(package->package.elements[0]); + 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)) + goto out; + + wakeup->gpe_device = + element->package.elements[0].reference.handle; + wakeup->gpe_number = + (u32) element->package.elements[1].integer.value; + } else if (element->type == ACPI_TYPE_INTEGER) { + wakeup->gpe_device = NULL; + wakeup->gpe_number = element->integer.value; + } else { + goto out; + } + + element = &(package->package.elements[1]); + if (element->type != ACPI_TYPE_INTEGER) + goto out; + + wakeup->sleep_state = element->integer.value; + + err = acpi_extract_power_resources(package, 2, &wakeup->resources); + if (err) + goto out; + + if (!list_empty(&wakeup->resources)) { + int sleep_state; + + err = acpi_power_wakeup_list_init(&wakeup->resources, + &sleep_state); + if (err) { + acpi_handle_warn(handle, "Retrieving current states " + "of wakeup power resources failed\n"); + acpi_power_resources_list_free(&wakeup->resources); + goto out; + } + 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; } - atomic_dec(&driver->references); - spin_lock(&acpi_device_lock); } - spin_unlock(&acpi_device_lock); + acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number); - Done: - return_VALUE(result); + out: + kfree(buffer.pointer); + return err; } -/* -------------------------------------------------------------------------- - Device Enumeration - -------------------------------------------------------------------------- */ +static void acpi_bus_set_run_wake_flags(struct acpi_device *device) +{ + struct acpi_device_id button_device_ids[] = { + {"PNP0C0C", 0}, + {"PNP0C0D", 0}, + {"PNP0C0E", 0}, + {"", 0}, + }; + acpi_status status; + acpi_event_status event_status; -static int acpi_bus_get_flags(struct acpi_device *device) + device->wakeup.flags.notifier_present = 0; + + /* Power button, Lid switch always enable wakeup */ + if (!acpi_match_device_ids(device, button_device_ids)) { + device->wakeup.flags.run_wake = 1; + if (!acpi_match_device_ids(device, &button_device_ids[1])) { + /* Do not use Lid/sleep button for S5 wakeup */ + if (device->wakeup.sleep_state == ACPI_STATE_S5) + device->wakeup.sleep_state = ACPI_STATE_S4; + } + device_set_wakeup_capable(&device->dev, true); + return; + } + + status = acpi_get_gpe_status(device->wakeup.gpe_device, + device->wakeup.gpe_number, + &event_status); + if (status == AE_OK) + device->wakeup.flags.run_wake = + !!(event_status & ACPI_EVENT_FLAG_HANDLE); +} + +static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) { - acpi_status status = AE_OK; - acpi_handle temp = NULL; + int err; + + /* Presence of _PRW indicates wake capable */ + if (!acpi_has_method(device->handle, "_PRW")) + return; + + 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; + } - ACPI_FUNCTION_TRACE("acpi_bus_get_flags"); + device->wakeup.flags.valid = 1; + device->wakeup.prepare_count = 0; + acpi_bus_set_run_wake_flags(device); + /* Call _PSW/_DSW object to disable its ability to wake the sleeping + * system for the ACPI device with the _PRW object. + * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW. + * So it is necessary to call _DSW object first. Only when it is not + * present will the _PSW object used. + */ + err = acpi_device_sleep_wake(device, 0, 0, 0); + if (err) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "error in _DSW or _PSW evaluation\n")); +} - /* Presence of _STA indicates 'dynamic_status' */ - status = acpi_get_handle(device->handle, "_STA", &temp); - if (ACPI_SUCCESS(status)) - device->flags.dynamic_status = 1; +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_status status; - /* Presence of _CID indicates 'compatible_ids' */ - status = acpi_get_handle(device->handle, "_CID", &temp); - if (ACPI_SUCCESS(status)) - device->flags.compatible_ids = 1; + INIT_LIST_HEAD(&ps->resources); - /* Presence of _RMV indicates 'removable' */ - status = acpi_get_handle(device->handle, "_RMV", &temp); - if (ACPI_SUCCESS(status)) - device->flags.removable = 1; + /* 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; - /* Presence of _EJD|_EJ0 indicates 'ejectable' */ - status = acpi_get_handle(device->handle, "_EJD", &temp); - if (ACPI_SUCCESS(status)) - device->flags.ejectable = 1; - else { - status = acpi_get_handle(device->handle, "_EJ0", &temp); - if (ACPI_SUCCESS(status)) - device->flags.ejectable = 1; + 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); } - /* Presence of _LCK indicates 'lockable' */ - status = acpi_get_handle(device->handle, "_LCK", &temp); - if (ACPI_SUCCESS(status)) - device->flags.lockable = 1; + /* Evaluate "_PSx" to see if we can do explicit sets */ + pathname[2] = 'S'; + if (acpi_has_method(device->handle, pathname)) + 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) +{ + u32 i; /* 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; + if (!acpi_has_method(device->handle, "_PS0") && + !acpi_has_method(device->handle, "_PR0")) + return; - /* Presence of _PRW indicates wake capable */ - status = acpi_get_handle(device->handle, "_PRW", &temp); - if (ACPI_SUCCESS(status)) - device->flags.wake_capable = 1; + device->flags.power_manageable = 1; + + /* + * Power Management Flags + */ + if (acpi_has_method(device->handle, "_PSC")) + device->power.flags.explicit_get = 1; + + if (acpi_has_method(device->handle, "_IRC")) + device->power.flags.inrush_current = 1; + + if (acpi_has_method(device->handle, "_DSW")) + device->power.flags.dsw_present = 1; + + /* + * Enumerate supported power management states + */ + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) + acpi_bus_init_power_state(device, i); + + 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; + device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; + device->power.states[ACPI_STATE_D3_COLD].power = 0; + + /* Set D3cold's explicit_set flag if _PS3 exists. */ + if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set) + device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1; + + /* Presence of _PS3 or _PRx means we can put the device into D3 cold */ + if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set || + device->power.flags.power_resources) + device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; + + if (acpi_bus_init_power(device)) { + acpi_free_power_resources_lists(device); + device->flags.power_manageable = 0; + } +} + +static void acpi_bus_get_flags(struct acpi_device *device) +{ + /* Presence of _STA indicates 'dynamic_status' */ + if (acpi_has_method(device->handle, "_STA")) + device->flags.dynamic_status = 1; - /* TBD: Peformance management */ + /* Presence of _RMV indicates 'removable' */ + if (acpi_has_method(device->handle, "_RMV")) + device->flags.removable = 1; - return_VALUE(0); + /* Presence of _EJD|_EJ0 indicates 'ejectable' */ + if (acpi_has_method(device->handle, "_EJD") || + acpi_has_method(device->handle, "_EJ0")) + device->flags.ejectable = 1; } -static void acpi_device_get_busid(struct acpi_device *device, - acpi_handle handle, int type) +static void acpi_device_get_busid(struct acpi_device *device) { char bus_id[5] = { '?', 0 }; struct acpi_buffer buffer = { sizeof(bus_id), bus_id }; @@ -765,10 +1615,12 @@ static void acpi_device_get_busid(struct acpi_device *device, * The device's Bus ID is simply the object name. * TBD: Shouldn't this value be unique (within the ACPI namespace)? */ - switch (type) { - case ACPI_BUS_TYPE_SYSTEM: + if (ACPI_IS_ROOT_DEVICE(device)) { strcpy(device->pnp.bus_id, "ACPI"); - break; + return; + } + + switch (device->device_type) { case ACPI_BUS_TYPE_POWER_BUTTON: strcpy(device->pnp.bus_id, "PWRF"); break; @@ -776,7 +1628,7 @@ static void acpi_device_get_busid(struct acpi_device *device, strcpy(device->pnp.bus_id, "SLPF"); break; default: - acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer); /* Clean up trailing underscores (if any) */ for (i = 3; i > 1; i--) { if (bus_id[i] == '_') @@ -789,620 +1641,693 @@ static void acpi_device_get_busid(struct acpi_device *device, } } -static void acpi_device_set_id(struct acpi_device *device, - struct acpi_device *parent, acpi_handle handle, - int type) +/* + * acpi_ata_match - see if an acpi object is an ATA device + * + * If an acpi object has one of the ACPI ATA methods defined, + * then we can safely call it an ATA device. + */ +bool acpi_ata_match(acpi_handle handle) +{ + return acpi_has_method(handle, "_GTF") || + acpi_has_method(handle, "_GTM") || + acpi_has_method(handle, "_STM") || + acpi_has_method(handle, "_SDD"); +} + +/* + * acpi_bay_match - see if an acpi object is an ejectable driver bay + * + * If an acpi object is ejectable and has one of the ACPI ATA methods defined, + * then we can safely call it an ejectable drive bay + */ +bool acpi_bay_match(acpi_handle handle) +{ + acpi_handle phandle; + + if (!acpi_has_method(handle, "_EJ0")) + return false; + if (acpi_ata_match(handle)) + return true; + if (ACPI_FAILURE(acpi_get_parent(handle, &phandle))) + return false; + + return acpi_ata_match(phandle); +} + +bool acpi_device_is_battery(struct acpi_device *adev) +{ + struct acpi_hardware_id *hwid; + + list_for_each_entry(hwid, &adev->pnp.ids, list) + if (!strcmp("PNP0C0A", hwid->id)) + return true; + + return false; +} + +static bool is_ejectable_bay(struct acpi_device *adev) +{ + acpi_handle handle = adev->handle; + + if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(adev)) + return true; + + return acpi_bay_match(handle); +} + +/* + * acpi_dock_match - see if an acpi object has a _DCK method + */ +bool acpi_dock_match(acpi_handle handle) +{ + return acpi_has_method(handle, "_DCK"); +} + +const char *acpi_device_hid(struct acpi_device *device) +{ + struct acpi_hardware_id *hid; + + if (list_empty(&device->pnp.ids)) + return dummy_hid; + + hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list); + return hid->id; +} +EXPORT_SYMBOL(acpi_device_hid); + +static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id) +{ + struct acpi_hardware_id *id; + + id = kmalloc(sizeof(*id), GFP_KERNEL); + if (!id) + return; + + id->id = kstrdup(dev_id, GFP_KERNEL); + if (!id->id) { + kfree(id); + return; + } + + list_add_tail(&id->list, &pnp->ids); + pnp->type.hardware_id = 1; +} + +/* + * Old IBM workstations have a DSDT bug wherein the SMBus object + * lacks the SMBUS01 HID and the methods do not have the necessary "_" + * prefix. Work around this. + */ +static bool acpi_ibm_smbus_match(acpi_handle handle) +{ + char node_name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer path = { sizeof(node_name), node_name }; + + if (!dmi_name_in_vendors("IBM")) + return false; + + /* Look for SMBS object */ + if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &path)) || + strcmp("SMBS", path.pointer)) + return false; + + /* Does it have the necessary (but misnamed) methods? */ + if (acpi_has_method(handle, "SBI") && + acpi_has_method(handle, "SBR") && + acpi_has_method(handle, "SBW")) + return true; + + return false; +} + +static bool acpi_object_is_system_bus(acpi_handle handle) +{ + acpi_handle tmp; + + if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_SB", &tmp)) && + tmp == handle) + return true; + if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_TZ", &tmp)) && + tmp == handle) + return true; + + return false; +} + +static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, + int device_type) { - struct acpi_device_info *info; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - char *hid = NULL; - char *uid = NULL; - struct acpi_compatible_id_list *cid_list = NULL; acpi_status status; + struct acpi_device_info *info; + struct acpi_pnp_device_id_list *cid_list; + int i; - switch (type) { + switch (device_type) { case ACPI_BUS_TYPE_DEVICE: - status = acpi_get_object_info(handle, &buffer); + if (handle == ACPI_ROOT_OBJECT) { + acpi_add_id(pnp, ACPI_SYSTEM_HID); + break; + } + + status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) { - printk("%s: Error reading device info\n", __FUNCTION__); + pr_err(PREFIX "%s: Error reading device info\n", + __func__); return; } - info = buffer.pointer; - if (info->valid & ACPI_VALID_HID) - hid = info->hardware_id.value; - if (info->valid & ACPI_VALID_UID) - uid = info->unique_id.value; - if (info->valid & ACPI_VALID_CID) - cid_list = &info->compatibility_id; + if (info->valid & ACPI_VALID_HID) { + acpi_add_id(pnp, info->hardware_id.string); + pnp->type.platform_id = 1; + } + if (info->valid & ACPI_VALID_CID) { + cid_list = &info->compatible_id_list; + for (i = 0; i < cid_list->count; i++) + acpi_add_id(pnp, cid_list->ids[i].string); + } if (info->valid & ACPI_VALID_ADR) { - device->pnp.bus_address = info->address; - device->flags.bus_address = 1; + pnp->bus_address = info->address; + pnp->type.bus_address = 1; } + if (info->valid & ACPI_VALID_UID) + pnp->unique_id = kstrdup(info->unique_id.string, + GFP_KERNEL); + + kfree(info); + + /* + * Some devices don't reliably have _HIDs & _CIDs, so add + * synthetic HIDs to make sure drivers can find them. + */ + if (acpi_is_video_device(handle)) + acpi_add_id(pnp, ACPI_VIDEO_HID); + else if (acpi_bay_match(handle)) + acpi_add_id(pnp, ACPI_BAY_HID); + else if (acpi_dock_match(handle)) + acpi_add_id(pnp, ACPI_DOCK_HID); + else if (acpi_ibm_smbus_match(handle)) + acpi_add_id(pnp, ACPI_SMBUS_IBM_HID); + else if (list_empty(&pnp->ids) && + acpi_object_is_system_bus(handle)) { + /* \_SB, \_TZ, LNXSYBUS */ + acpi_add_id(pnp, ACPI_BUS_HID); + strcpy(pnp->device_name, ACPI_BUS_DEVICE_NAME); + strcpy(pnp->device_class, ACPI_BUS_CLASS); + } + break; case ACPI_BUS_TYPE_POWER: - hid = ACPI_POWER_HID; + acpi_add_id(pnp, ACPI_POWER_HID); break; case ACPI_BUS_TYPE_PROCESSOR: - hid = ACPI_PROCESSOR_HID; - break; - case ACPI_BUS_TYPE_SYSTEM: - hid = ACPI_SYSTEM_HID; + acpi_add_id(pnp, ACPI_PROCESSOR_OBJECT_HID); break; case ACPI_BUS_TYPE_THERMAL: - hid = ACPI_THERMAL_HID; + acpi_add_id(pnp, ACPI_THERMAL_HID); break; case ACPI_BUS_TYPE_POWER_BUTTON: - hid = ACPI_BUTTON_HID_POWERF; + acpi_add_id(pnp, ACPI_BUTTON_HID_POWERF); break; case ACPI_BUS_TYPE_SLEEP_BUTTON: - hid = ACPI_BUTTON_HID_SLEEPF; + acpi_add_id(pnp, ACPI_BUTTON_HID_SLEEPF); break; } +} - /* - * \_SB - * ---- - * Fix for the system root bus device -- the only root-level device. - */ - if ((parent == ACPI_ROOT_OBJECT) && (type == ACPI_BUS_TYPE_DEVICE)) { - hid = ACPI_BUS_HID; - strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); - strcpy(device->pnp.device_class, ACPI_BUS_CLASS); - } +void acpi_free_pnp_ids(struct acpi_device_pnp *pnp) +{ + struct acpi_hardware_id *id, *tmp; - if (hid) { - strcpy(device->pnp.hardware_id, hid); - device->flags.hardware_id = 1; - } - if (uid) { - strcpy(device->pnp.unique_id, uid); - device->flags.unique_id = 1; - } - if (cid_list) { - device->pnp.cid_list = kmalloc(cid_list->size, GFP_KERNEL); - if (device->pnp.cid_list) - memcpy(device->pnp.cid_list, cid_list, cid_list->size); - else - printk(KERN_ERR "Memory allocation error\n"); + list_for_each_entry_safe(id, tmp, &pnp->ids, list) { + kfree(id->id); + kfree(id); } + kfree(pnp->unique_id); +} - acpi_os_free(buffer.pointer); +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; + device->handle = handle; + device->parent = acpi_bus_get_parent(handle); + acpi_set_device_status(device, sta); + acpi_device_get_busid(device); + acpi_set_pnp_ids(handle, &device->pnp, type); + acpi_bus_get_flags(device); + device->flags.match_driver = false; + device->flags.initialized = true; + device->flags.visited = false; + device_initialize(&device->dev); + dev_set_uevent_suppress(&device->dev, true); } -static int acpi_device_set_context(struct acpi_device *device, int type) +void acpi_device_add_finalize(struct acpi_device *device) { - acpi_status status = AE_OK; - int result = 0; - /* - * Context - * ------- - * Attach this 'struct acpi_device' to the ACPI object. This makes - * resolutions from handle->device very efficient. Note that we need - * to be careful with fixed-feature devices as they all attach to the - * root object. - */ - if (type != ACPI_BUS_TYPE_POWER_BUTTON && - type != ACPI_BUS_TYPE_SLEEP_BUTTON) { - status = acpi_attach_data(device->handle, - acpi_bus_data_handler, device); + dev_set_uevent_suppress(&device->dev, false); + kobject_uevent(&device->dev.kobj, KOBJ_ADD); +} - if (ACPI_FAILURE(status)) { - printk("Error attaching device data\n"); - result = -ENODEV; - } +static int acpi_add_single_object(struct acpi_device **child, + acpi_handle handle, int type, + unsigned long long sta) +{ + int result; + struct acpi_device *device; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL); + if (!device) { + printk(KERN_ERR PREFIX "Memory allocation error\n"); + return -ENOMEM; } - return result; + + acpi_init_device_object(device, handle, type, sta); + acpi_bus_get_power_flags(device); + acpi_bus_get_wakeup_device_flags(device); + + 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, + device->parent ? dev_name(&device->parent->dev) : "(null)")); + kfree(buffer.pointer); + *child = device; + return 0; } -static void acpi_device_get_debug_info(struct acpi_device *device, - acpi_handle handle, int type) +static int acpi_bus_type_and_status(acpi_handle handle, int *type, + unsigned long long *sta) { -#ifdef CONFIG_ACPI_DEBUG_OUTPUT - char *type_string = NULL; - char name[80] = { '?', '\0' }; - struct acpi_buffer buffer = { sizeof(name), name }; + acpi_status status; + acpi_object_type acpi_type; - switch (type) { - case ACPI_BUS_TYPE_DEVICE: - type_string = "Device"; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - break; - case ACPI_BUS_TYPE_POWER: - type_string = "Power Resource"; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - break; - case ACPI_BUS_TYPE_PROCESSOR: - type_string = "Processor"; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - break; - case ACPI_BUS_TYPE_SYSTEM: - type_string = "System"; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + status = acpi_get_type(handle, &acpi_type); + if (ACPI_FAILURE(status)) + return -ENODEV; + + switch (acpi_type) { + case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */ + case ACPI_TYPE_DEVICE: + *type = ACPI_BUS_TYPE_DEVICE; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + return -ENODEV; break; - case ACPI_BUS_TYPE_THERMAL: - type_string = "Thermal Zone"; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + case ACPI_TYPE_PROCESSOR: + *type = ACPI_BUS_TYPE_PROCESSOR; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + return -ENODEV; break; - case ACPI_BUS_TYPE_POWER_BUTTON: - type_string = "Power Button"; - sprintf(name, "PWRB"); + case ACPI_TYPE_THERMAL: + *type = ACPI_BUS_TYPE_THERMAL; + *sta = ACPI_STA_DEFAULT; break; - case ACPI_BUS_TYPE_SLEEP_BUTTON: - type_string = "Sleep Button"; - sprintf(name, "SLPB"); + case ACPI_TYPE_POWER: + *type = ACPI_BUS_TYPE_POWER; + *sta = ACPI_STA_DEFAULT; break; + default: + return -ENODEV; } - printk(KERN_DEBUG "Found %s %s [%p]\n", type_string, name, handle); -#endif /*CONFIG_ACPI_DEBUG_OUTPUT */ + return 0; } -static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) +bool acpi_device_is_present(struct acpi_device *adev) { - int result = 0; - struct acpi_driver *driver; - - ACPI_FUNCTION_TRACE("acpi_bus_remove"); + if (adev->status.present || adev->status.functional) + return true; - if (!dev) - return_VALUE(-EINVAL); + adev->flags.initialized = false; + return false; +} - driver = dev->driver; +static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, + char *idstr, + const struct acpi_device_id **matchid) +{ + const struct acpi_device_id *devid; - if ((driver) && (driver->ops.remove)) { + if (handler->match) + return handler->match(idstr, matchid); - if (driver->ops.stop) { - result = driver->ops.stop(dev, ACPI_BUS_REMOVAL_EJECT); - if (result) - return_VALUE(result); - } + for (devid = handler->ids; devid->id[0]; devid++) + if (!strcmp((char *)devid->id, idstr)) { + if (matchid) + *matchid = devid; - result = dev->driver->ops.remove(dev, ACPI_BUS_REMOVAL_EJECT); - if (result) { - return_VALUE(result); + return true; } - atomic_dec(&dev->driver->references); - dev->driver = NULL; - acpi_driver_data(dev) = NULL; - } - - if (!rmdevice) - return_VALUE(0); + return false; +} - if (dev->flags.bus_address) { - if ((dev->parent) && (dev->parent->ops.unbind)) - dev->parent->ops.unbind(dev); - } +static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, + const struct acpi_device_id **matchid) +{ + struct acpi_scan_handler *handler; - acpi_device_unregister(dev, ACPI_BUS_REMOVAL_EJECT); + list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) + if (acpi_scan_handler_matching(handler, idstr, matchid)) + return handler; - return_VALUE(0); + return NULL; } -static int -acpi_add_single_object(struct acpi_device **child, - struct acpi_device *parent, acpi_handle handle, int type) +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val) { - int result = 0; - struct acpi_device *device = NULL; - - ACPI_FUNCTION_TRACE("acpi_add_single_object"); + if (!!hotplug->enabled == !!val) + return; - if (!child) - return_VALUE(-EINVAL); + mutex_lock(&acpi_scan_lock); - device = kmalloc(sizeof(struct acpi_device), GFP_KERNEL); - if (!device) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Memory allocation error\n")); - return_VALUE(-ENOMEM); - } - memset(device, 0, sizeof(struct acpi_device)); + hotplug->enabled = val; - device->handle = handle; - device->parent = parent; + mutex_unlock(&acpi_scan_lock); +} - acpi_device_get_busid(device, handle, type); +static void acpi_scan_init_hotplug(struct acpi_device *adev) +{ + struct acpi_hardware_id *hwid; - /* - * Flags - * ----- - * Get prior to calling acpi_bus_get_status() so we know whether - * or not _STA is present. 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; + if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev)) { + acpi_dock_add(adev); + return; + } + list_for_each_entry(hwid, &adev->pnp.ids, list) { + struct acpi_scan_handler *handler; - /* - * Status - * ------ - * See if the device is present. We always assume that non-Device - * and non-Processor objects (e.g. thermal zones, power resources, - * etc.) are present, functioning, etc. (at least when parent object - * is present). Note that _STA has a different meaning for some - * objects (e.g. power resources) so we need to be careful how we use - * it. - */ - switch (type) { - case ACPI_BUS_TYPE_PROCESSOR: - case ACPI_BUS_TYPE_DEVICE: - result = acpi_bus_get_status(device); - if (ACPI_FAILURE(result) || !device->status.present) { - result = -ENOENT; - goto end; + handler = acpi_scan_match_handler(hwid->id, NULL); + if (handler) { + adev->flags.hotplug_notify = true; + break; } - break; - default: - STRUCT_TO_INT(device->status) = 0x0F; - break; } +} - /* - * Initialize Device - * ----------------- - * TBD: Synch with Core's enumeration/initialization process. - */ +static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **return_value) +{ + struct acpi_device *device = NULL; + int type; + unsigned long long sta; + int result; - /* - * Hardware ID, Unique ID, & Bus Address - * ------------------------------------- - */ - acpi_device_set_id(device, parent, handle, type); + acpi_bus_get_device(handle, &device); + if (device) + goto out; - /* - * Power Management - * ---------------- - */ - if (device->flags.power_manageable) { - result = acpi_bus_get_power_flags(device); - if (result) - goto end; - } + result = acpi_bus_type_and_status(handle, &type, &sta); + if (result) + return AE_OK; - /* - * Wakeup device management - *----------------------- - */ - if (device->flags.wake_capable) { - result = acpi_bus_get_wakeup_device_flags(device); - if (result) - goto end; + if (type == ACPI_BUS_TYPE_POWER) { + acpi_add_power_resource(handle); + return AE_OK; } - /* - * Performance Management - * ---------------------- - */ - if (device->flags.performance_manageable) { - result = acpi_bus_get_perf_flags(device); - if (result) - goto end; - } + acpi_add_single_object(&device, handle, type, sta); + if (!device) + return AE_CTRL_DEPTH; - if ((result = acpi_device_set_context(device, type))) - goto end; + acpi_scan_init_hotplug(device); - acpi_device_get_debug_info(device, handle, type); + out: + if (!*return_value) + *return_value = device; - acpi_device_register(device, parent); + return AE_OK; +} - /* - * Bind _ADR-Based Devices - * ----------------------- - * If there's a a bus address (_ADR) then we utilize the parent's - * 'bind' function (if exists) to bind the ACPI- and natively- - * enumerated device representations. - */ - if (device->flags.bus_address) { - if (device->parent && device->parent->ops.bind) - device->parent->ops.bind(device); - } +static int acpi_check_spi_i2c_slave(struct acpi_resource *ares, void *data) +{ + bool *is_spi_i2c_slave_p = data; + + if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return 1; /* - * Locate & Attach Driver - * ---------------------- - * If there's a hardware id (_HID) or compatible ids (_CID) we check - * to see if there's a driver installed for this kind of device. Note - * that drivers can install before or after a device is enumerated. - * - * TBD: Assumes LDM provides driver hot-plug capability. + * devices that are connected to UART still need to be enumerated to + * platform bus */ - result = acpi_bus_find_driver(device); - - end: - if (!result) - *child = device; - else { - if (device->pnp.cid_list) - kfree(device->pnp.cid_list); - kfree(device); - } + if (ares->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART) + *is_spi_i2c_slave_p = true; - return_VALUE(result); + /* no need to do more checking */ + return -1; } -static int acpi_bus_scan(struct acpi_device *start, struct acpi_bus_ops *ops) +static void acpi_default_enumeration(struct acpi_device *device) { - acpi_status status = AE_OK; - struct acpi_device *parent = NULL; - struct acpi_device *child = NULL; - acpi_handle phandle = NULL; - acpi_handle chandle = NULL; - acpi_object_type type = 0; - u32 level = 1; + struct list_head resource_list; + bool is_spi_i2c_slave = false; - ACPI_FUNCTION_TRACE("acpi_bus_scan"); - - if (!start) - return_VALUE(-EINVAL); - - parent = start; - phandle = start->handle; + if (!device->pnp.type.platform_id || device->handler) + return; /* - * Parse through the ACPI namespace, identify all 'devices', and - * create a new 'struct acpi_device' for each. + * Do not enemerate SPI/I2C slaves as they will be enuerated by their + * respective parents. */ - while ((level > 0) && parent) { - - status = acpi_get_next_object(ACPI_TYPE_ANY, phandle, - chandle, &chandle); - - /* - * If this scope is exhausted then move our way back up. - */ - if (ACPI_FAILURE(status)) { - level--; - chandle = phandle; - acpi_get_parent(phandle, &phandle); - if (parent->parent) - parent = parent->parent; - continue; - } - - status = acpi_get_type(chandle, &type); - if (ACPI_FAILURE(status)) - continue; - - /* - * If this is a scope object then parse it (depth-first). - */ - if (type == ACPI_TYPE_LOCAL_SCOPE) { - level++; - phandle = chandle; - chandle = NULL; - continue; - } + INIT_LIST_HEAD(&resource_list); + acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave, + &is_spi_i2c_slave); + acpi_dev_free_resource_list(&resource_list); + if (!is_spi_i2c_slave) + acpi_create_platform_device(device); +} - /* - * We're only interested in objects that we consider 'devices'. - */ - switch (type) { - case ACPI_TYPE_DEVICE: - type = ACPI_BUS_TYPE_DEVICE; - break; - case ACPI_TYPE_PROCESSOR: - type = ACPI_BUS_TYPE_PROCESSOR; - break; - case ACPI_TYPE_THERMAL: - type = ACPI_BUS_TYPE_THERMAL; - break; - case ACPI_TYPE_POWER: - type = ACPI_BUS_TYPE_POWER; - break; - default: - continue; - } +static int acpi_scan_attach_handler(struct acpi_device *device) +{ + struct acpi_hardware_id *hwid; + int ret = 0; - if (ops->acpi_op_add) - status = acpi_add_single_object(&child, parent, - chandle, type); - else - status = acpi_bus_get_device(chandle, &child); + list_for_each_entry(hwid, &device->pnp.ids, list) { + const struct acpi_device_id *devid; + struct acpi_scan_handler *handler; - if (ACPI_FAILURE(status)) - continue; - - if (ops->acpi_op_start) { - status = acpi_start_single_object(child); - if (ACPI_FAILURE(status)) + handler = acpi_scan_match_handler(hwid->id, &devid); + if (handler) { + if (!handler->attach) { + device->pnp.type.platform_id = 0; continue; - } - - /* - * If the device is present, enabled, and functioning then - * parse its scope (depth-first). Note that we need to - * represent absent devices to facilitate PnP notifications - * -- but only the subtree head (not all of its children, - * which will be enumerated when the parent is inserted). - * - * TBD: Need notifications and other detection mechanisms - * in place before we can fully implement this. - */ - if (child->status.present) { - status = acpi_get_next_object(ACPI_TYPE_ANY, chandle, - NULL, NULL); - if (ACPI_SUCCESS(status)) { - level++; - phandle = chandle; - chandle = NULL; - parent = child; } + device->handler = handler; + ret = handler->attach(device, devid); + if (ret > 0) + break; + + device->handler = NULL; + if (ret < 0) + break; } } + if (!ret) + acpi_default_enumeration(device); - return_VALUE(0); + return ret; } -int -acpi_bus_add(struct acpi_device **child, - struct acpi_device *parent, acpi_handle handle, int type) +static void acpi_bus_attach(struct acpi_device *device) { - int result; - struct acpi_bus_ops ops; - - ACPI_FUNCTION_TRACE("acpi_bus_add"); + struct acpi_device *child; + acpi_handle ejd; + int ret; + + if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) + register_dock_dependent_device(device, ejd); + + acpi_bus_get_status(device); + /* Skip devices that are not present. */ + if (!acpi_device_is_present(device)) { + device->flags.visited = false; + return; + } + if (device->handler) + goto ok; - result = acpi_add_single_object(child, parent, handle, type); - if (!result) { - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_add = 1; - result = acpi_bus_scan(*child, &ops); + if (!device->flags.initialized) { + acpi_bus_update_power(device, NULL); + device->flags.initialized = true; } - return_VALUE(result); -} + device->flags.visited = false; + ret = acpi_scan_attach_handler(device); + if (ret < 0) + return; + + device->flags.match_driver = true; + if (!ret) { + ret = device_attach(&device->dev); + if (ret < 0) + return; + } + device->flags.visited = true; -EXPORT_SYMBOL(acpi_bus_add); + ok: + list_for_each_entry(child, &device->children, node) + acpi_bus_attach(child); +} -int acpi_bus_start(struct acpi_device *device) +/** + * acpi_bus_scan - Add ACPI device node objects in a given namespace scope. + * @handle: Root of the namespace scope to scan. + * + * Scan a given ACPI tree (probably recently hot-plugged) and create and add + * found devices. + * + * If no devices were found, -ENODEV is returned, but it does not mean that + * 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) { - int result; - struct acpi_bus_ops ops; - - ACPI_FUNCTION_TRACE("acpi_bus_start"); + void *device = NULL; - if (!device) - return_VALUE(-EINVAL); + 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); - result = acpi_start_single_object(device); - if (!result) { - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_start = 1; - result = acpi_bus_scan(device, &ops); + if (device) { + acpi_bus_attach(device); + return 0; } - return_VALUE(result); + return -ENODEV; } +EXPORT_SYMBOL(acpi_bus_scan); -EXPORT_SYMBOL(acpi_bus_start); - -static int acpi_bus_trim(struct acpi_device *start, int rmdevice) +/** + * acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects. + * @adev: Root of the ACPI namespace scope to walk. + * + * Must be called under acpi_scan_lock. + */ +void acpi_bus_trim(struct acpi_device *adev) { - acpi_status status; - struct acpi_device *parent, *child; - acpi_handle phandle, chandle; - acpi_object_type type; - u32 level = 1; - int err = 0; + struct acpi_scan_handler *handler = adev->handler; + struct acpi_device *child; - parent = start; - phandle = start->handle; - child = chandle = NULL; + list_for_each_entry_reverse(child, &adev->children, node) + acpi_bus_trim(child); - while ((level > 0) && parent && (!err)) { - status = acpi_get_next_object(ACPI_TYPE_ANY, phandle, - chandle, &chandle); + adev->flags.match_driver = false; + if (handler) { + if (handler->detach) + handler->detach(adev); - /* - * If this scope is exhausted then move our way back up. - */ - if (ACPI_FAILURE(status)) { - level--; - chandle = phandle; - acpi_get_parent(phandle, &phandle); - child = parent; - parent = parent->parent; - - if (level == 0) - err = acpi_bus_remove(child, rmdevice); - else - err = acpi_bus_remove(child, 1); - - continue; - } - - status = acpi_get_type(chandle, &type); - if (ACPI_FAILURE(status)) { - continue; - } - /* - * If there is a device corresponding to chandle then - * parse it (depth-first). - */ - if (acpi_bus_get_device(chandle, &child) == 0) { - level++; - phandle = chandle; - chandle = NULL; - parent = child; - } - continue; + adev->handler = NULL; + } else { + device_release_driver(&adev->dev); } - return err; + /* + * Most likely, the device is going away, so put it into D3cold before + * that. + */ + acpi_device_set_power(adev, ACPI_STATE_D3_COLD); + adev->flags.initialized = false; + adev->flags.visited = false; } +EXPORT_SYMBOL_GPL(acpi_bus_trim); -static int acpi_bus_scan_fixed(struct acpi_device *root) +static int acpi_bus_scan_fixed(void) { int result = 0; - struct acpi_device *device = NULL; - - ACPI_FUNCTION_TRACE("acpi_bus_scan_fixed"); - - if (!root) - return_VALUE(-ENODEV); /* * Enumerate all fixed-feature devices. */ - if (acpi_fadt.pwr_button == 0) { - result = acpi_add_single_object(&device, acpi_root, - NULL, - ACPI_BUS_TYPE_POWER_BUTTON); - if (!result) - result = acpi_start_single_object(device); + 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); + if (result) + return result; + + device->flags.match_driver = true; + result = device_attach(&device->dev); + if (result < 0) + return result; + + device_init_wakeup(&device->dev, true); } - if (acpi_fadt.sleep_button == 0) { - result = acpi_add_single_object(&device, acpi_root, - NULL, - ACPI_BUS_TYPE_SLEEP_BUTTON); - if (!result) - result = acpi_start_single_object(device); + 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); + if (result) + return result; + + device->flags.match_driver = true; + result = device_attach(&device->dev); } - return_VALUE(result); + return result < 0 ? result : 0; } -static int __init acpi_scan_init(void) +int __init acpi_scan_init(void) { int result; - struct acpi_bus_ops ops; - - ACPI_FUNCTION_TRACE("acpi_scan_init"); - if (acpi_disabled) - return_VALUE(0); + result = bus_register(&acpi_bus_type); + if (result) { + /* We don't want to quit even if we failed to add suspend/resume */ + printk(KERN_ERR PREFIX "Could not register bus type\n"); + } - kset_register(&acpi_namespace_kset); + acpi_pci_root_init(); + acpi_pci_link_init(); + acpi_processor_init(); + acpi_lpss_init(); + acpi_cmos_rtc_init(); + acpi_container_init(); + acpi_memory_hotplug_init(); + acpi_pnp_init(); + mutex_lock(&acpi_scan_lock); /* - * Create the root device in the bus's device tree + * Enumerate devices in the ACPI namespace. */ - result = acpi_add_single_object(&acpi_root, NULL, ACPI_ROOT_OBJECT, - ACPI_BUS_TYPE_SYSTEM); + result = acpi_bus_scan(ACPI_ROOT_OBJECT); if (result) - goto Done; + goto out; - result = acpi_start_single_object(acpi_root); + result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root); + if (result) + goto out; - /* - * Enumerate devices in the ACPI namespace. - */ - result = acpi_bus_scan_fixed(acpi_root); - if (!result) { - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_add = 1; - ops.acpi_op_start = 1; - result = acpi_bus_scan(acpi_root, &ops); + /* Fixed feature devices do not exist on HW-reduced platform */ + if (!acpi_gbl_reduced_hardware) { + result = acpi_bus_scan_fixed(); + if (result) { + acpi_detach_data(acpi_root->handle, + acpi_scan_drop_device); + acpi_device_del(acpi_root); + put_device(&acpi_root->dev); + goto out; + } } - if (result) - acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); + acpi_update_all_gpes(); - Done: - return_VALUE(result); + out: + mutex_unlock(&acpi_scan_lock); + return result; } - -subsys_initcall(acpi_scan_init); |
