diff options
Diffstat (limited to 'drivers/acpi/glue.c')
-rw-r--r-- | drivers/acpi/glue.c | 136 |
1 files changed, 87 insertions, 49 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 243ee85e4d2e..08373086cd7e 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -25,6 +25,8 @@ static LIST_HEAD(bus_type_list); static DECLARE_RWSEM(bus_type_sem); +#define PHYSICAL_NODE_STRING "physical_node" + int register_acpi_bus_type(struct acpi_bus_type *type) { if (acpi_disabled) @@ -124,84 +126,120 @@ acpi_handle acpi_get_child(acpi_handle parent, u64 address) EXPORT_SYMBOL(acpi_get_child); -/* Link ACPI devices with physical devices */ -static void acpi_glue_data_handler(acpi_handle handle, - void *context) -{ - /* we provide an empty handler */ -} - -/* Note: a success call will increase reference count by one */ -struct device *acpi_get_physical_device(acpi_handle handle) -{ - acpi_status status; - struct device *dev; - - status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev); - if (ACPI_SUCCESS(status)) - return get_device(dev); - return NULL; -} - -EXPORT_SYMBOL(acpi_get_physical_device); - static int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; acpi_status status; + struct acpi_device_physical_node *physical_node; + char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; + int retval = -EINVAL; if (dev->archdata.acpi_handle) { dev_warn(dev, "Drivers changed 'acpi_handle'\n"); return -EINVAL; } + get_device(dev); - status = acpi_attach_data(handle, acpi_glue_data_handler, dev); - if (ACPI_FAILURE(status)) { - put_device(dev); - return -EINVAL; + status = acpi_bus_get_device(handle, &acpi_dev); + if (ACPI_FAILURE(status)) + goto err; + + physical_node = kzalloc(sizeof(struct acpi_device_physical_node), + GFP_KERNEL); + if (!physical_node) { + retval = -ENOMEM; + goto err; } - dev->archdata.acpi_handle = handle; - status = acpi_bus_get_device(handle, &acpi_dev); - if (!ACPI_FAILURE(status)) { - int ret; - - ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, - "firmware_node"); - ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, - "physical_node"); - if (acpi_dev->wakeup.flags.valid) - device_set_wakeup_capable(dev, true); + mutex_lock(&acpi_dev->physical_node_lock); + /* allocate physical node id according to physical_node_id_bitmap */ + physical_node->node_id = + find_first_zero_bit(acpi_dev->physical_node_id_bitmap, + ACPI_MAX_PHYSICAL_NODE); + if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) { + retval = -ENOSPC; + mutex_unlock(&acpi_dev->physical_node_lock); + kfree(physical_node); + goto err; } + set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap); + physical_node->dev = dev; + list_add_tail(&physical_node->node, &acpi_dev->physical_node_list); + acpi_dev->physical_node_count++; + mutex_unlock(&acpi_dev->physical_node_lock); + + dev->archdata.acpi_handle = 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); + retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, + physical_node_name); + retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, + "firmware_node"); + + if (acpi_dev->wakeup.flags.valid) + device_set_wakeup_capable(dev, true); + return 0; + + err: + put_device(dev); + return retval; } static 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 (!dev->archdata.acpi_handle) return 0; - if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) { - struct acpi_device *acpi_dev; - /* acpi_get_physical_device increase refcnt by one */ - put_device(dev); + status = acpi_bus_get_device(dev->archdata.acpi_handle, + &acpi_dev); + if (ACPI_FAILURE(status)) + goto err; - if (!acpi_bus_get_device(dev->archdata.acpi_handle, - &acpi_dev)) { - sysfs_remove_link(&dev->kobj, "firmware_node"); - sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node"); - } + mutex_lock(&acpi_dev->physical_node_lock); + list_for_each_safe(node, next, &acpi_dev->physical_node_list) { + char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; + + entry = list_entry(node, struct acpi_device_physical_node, + node); + if (entry->dev != dev) + continue; + + list_del(node); + clear_bit(entry->node_id, acpi_dev->physical_node_id_bitmap); - acpi_detach_data(dev->archdata.acpi_handle, - acpi_glue_data_handler); + acpi_dev->physical_node_count--; + + 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"); dev->archdata.acpi_handle = NULL; /* acpi_bind_one increase refcnt by one */ put_device(dev); - } else { - dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); + kfree(entry); } + mutex_unlock(&acpi_dev->physical_node_lock); + return 0; + +err: + dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); + return -EINVAL; } static int acpi_platform_notify(struct device *dev) |