diff options
Diffstat (limited to 'drivers/acpi/glue.c')
-rw-r--r-- | drivers/acpi/glue.c | 126 |
1 files changed, 71 insertions, 55 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 408f6b2a5fa8..10f0f40587bb 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -79,6 +79,9 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) return ret; } +#define FIND_CHILD_MIN_SCORE 1 +#define FIND_CHILD_MAX_SCORE 2 + static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used, void *not_used, void **ret_p) { @@ -92,14 +95,17 @@ static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge) +static int do_find_child_checks(acpi_handle handle, bool is_bridge) { + bool sta_present = true; unsigned long long sta; acpi_status status; - status = acpi_bus_get_status_handle(handle, &sta); - if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) - return false; + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (status == AE_NOT_FOUND) + sta_present = false; + else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) + return -ENODEV; if (is_bridge) { void *test = NULL; @@ -107,16 +113,17 @@ static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge) /* Check if this object has at least one child device. */ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, acpi_dev_present, NULL, NULL, &test); - return !!test; + if (!test) + return -ENODEV; } - return true; + return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE; } struct find_child_context { u64 addr; bool is_bridge; acpi_handle ret; - bool ret_checked; + int ret_score; }; static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, @@ -125,6 +132,7 @@ static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, struct find_child_context *context = data; unsigned long long addr; acpi_status status; + int score; status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); if (ACPI_FAILURE(status) || addr != context->addr) @@ -144,15 +152,20 @@ static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, * its handle if so. Second, check the same for the object that we've * just found. */ - if (!context->ret_checked) { - if (acpi_extra_checks_passed(context->ret, context->is_bridge)) + if (!context->ret_score) { + score = do_find_child_checks(context->ret, context->is_bridge); + if (score == FIND_CHILD_MAX_SCORE) return AE_CTRL_TERMINATE; else - context->ret_checked = true; + context->ret_score = score; } - if (acpi_extra_checks_passed(handle, context->is_bridge)) { + score = do_find_child_checks(handle, context->is_bridge); + if (score == FIND_CHILD_MAX_SCORE) { context->ret = handle; return AE_CTRL_TERMINATE; + } else if (score > context->ret_score) { + context->ret = handle; + context->ret_score = score; } return AE_OK; } @@ -173,6 +186,15 @@ acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge) } EXPORT_SYMBOL_GPL(acpi_find_child); +static void acpi_physnode_link_name(char *buf, unsigned int node_id) +{ + if (node_id > 0) + snprintf(buf, PHYSICAL_NODE_NAME_SIZE, + PHYSICAL_NODE_STRING "%u", node_id); + else + strcpy(buf, PHYSICAL_NODE_STRING); +} + int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; @@ -216,8 +238,15 @@ int acpi_bind_one(struct device *dev, acpi_handle handle) list_for_each_entry(pn, &acpi_dev->physical_node_list, node) { /* Sanity check. */ if (pn->dev == dev) { + mutex_unlock(&acpi_dev->physical_node_lock); + dev_warn(dev, "Already associated with ACPI node\n"); - goto err_free; + kfree(physical_node); + if (ACPI_HANDLE(dev) != handle) + goto err; + + put_device(dev); + return 0; } if (pn->node_id == node_id) { physnode_list = &pn->node; @@ -230,20 +259,23 @@ int acpi_bind_one(struct device *dev, acpi_handle handle) list_add(&physical_node->node, physnode_list); acpi_dev->physical_node_count++; - mutex_unlock(&acpi_dev->physical_node_lock); - if (!ACPI_HANDLE(dev)) ACPI_HANDLE_SET(dev, acpi_dev->handle); - if (!physical_node->node_id) - strcpy(physical_node_name, PHYSICAL_NODE_STRING); - else - sprintf(physical_node_name, - "physical_node%d", physical_node->node_id); + acpi_physnode_link_name(physical_node_name, node_id); retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, - physical_node_name); + physical_node_name); + if (retval) + dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n", + physical_node_name, retval); + retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, - "firmware_node"); + "firmware_node"); + if (retval) + dev_err(dev, "Failed to create link firmware_node (%d)\n", + retval); + + mutex_unlock(&acpi_dev->physical_node_lock); if (acpi_dev->wakeup.flags.valid) device_set_wakeup_capable(dev, true); @@ -254,11 +286,6 @@ int acpi_bind_one(struct device *dev, acpi_handle handle) ACPI_HANDLE_SET(dev, NULL); put_device(dev); return retval; - - err_free: - mutex_unlock(&acpi_dev->physical_node_lock); - kfree(physical_node); - goto err; } EXPORT_SYMBOL_GPL(acpi_bind_one); @@ -267,48 +294,37 @@ int acpi_unbind_one(struct device *dev) struct acpi_device_physical_node *entry; struct acpi_device *acpi_dev; acpi_status status; - struct list_head *node, *next; if (!ACPI_HANDLE(dev)) return 0; status = acpi_bus_get_device(ACPI_HANDLE(dev), &acpi_dev); - if (ACPI_FAILURE(status)) - goto err; + if (ACPI_FAILURE(status)) { + dev_err(dev, "Oops, ACPI handle corrupt in %s()\n", __func__); + return -EINVAL; + } mutex_lock(&acpi_dev->physical_node_lock); - list_for_each_safe(node, next, &acpi_dev->physical_node_list) { - char physical_node_name[PHYSICAL_NODE_NAME_SIZE]; - entry = list_entry(node, struct acpi_device_physical_node, - node); - if (entry->dev != dev) - continue; + list_for_each_entry(entry, &acpi_dev->physical_node_list, node) + if (entry->dev == dev) { + char physnode_name[PHYSICAL_NODE_NAME_SIZE]; - list_del(node); + list_del(&entry->node); + acpi_dev->physical_node_count--; - acpi_dev->physical_node_count--; + acpi_physnode_link_name(physnode_name, entry->node_id); + sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name); + sysfs_remove_link(&dev->kobj, "firmware_node"); + ACPI_HANDLE_SET(dev, NULL); + /* acpi_bind_one() increase refcnt by one. */ + put_device(dev); + kfree(entry); + break; + } - if (!entry->node_id) - strcpy(physical_node_name, PHYSICAL_NODE_STRING); - else - sprintf(physical_node_name, - "physical_node%d", entry->node_id); - - sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name); - sysfs_remove_link(&dev->kobj, "firmware_node"); - ACPI_HANDLE_SET(dev, NULL); - /* acpi_bind_one increase refcnt by one */ - put_device(dev); - kfree(entry); - } mutex_unlock(&acpi_dev->physical_node_lock); - return 0; - -err: - dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); - return -EINVAL; } EXPORT_SYMBOL_GPL(acpi_unbind_one); |