linux/drivers/acpi/glue.c
<<
>>
Prefs
   1/*
   2 * Link physical devices with ACPI devices support
   3 *
   4 * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com>
   5 * Copyright (c) 2005 Intel Corp.
   6 *
   7 * This file is released under the GPLv2.
   8 */
   9#include <linux/init.h>
  10#include <linux/list.h>
  11#include <linux/device.h>
  12#include <linux/slab.h>
  13#include <linux/rwsem.h>
  14#include <linux/acpi.h>
  15
  16#include "internal.h"
  17
  18#define ACPI_GLUE_DEBUG 0
  19#if ACPI_GLUE_DEBUG
  20#define DBG(x...) printk(PREFIX x)
  21#else
  22#define DBG(x...) do { } while(0)
  23#endif
  24static LIST_HEAD(bus_type_list);
  25static DECLARE_RWSEM(bus_type_sem);
  26
  27int register_acpi_bus_type(struct acpi_bus_type *type)
  28{
  29        if (acpi_disabled)
  30                return -ENODEV;
  31        if (type && type->bus && type->find_device) {
  32                down_write(&bus_type_sem);
  33                list_add_tail(&type->list, &bus_type_list);
  34                up_write(&bus_type_sem);
  35                printk(KERN_INFO PREFIX "bus type %s registered\n",
  36                       type->bus->name);
  37                return 0;
  38        }
  39        return -ENODEV;
  40}
  41
  42int unregister_acpi_bus_type(struct acpi_bus_type *type)
  43{
  44        if (acpi_disabled)
  45                return 0;
  46        if (type) {
  47                down_write(&bus_type_sem);
  48                list_del_init(&type->list);
  49                up_write(&bus_type_sem);
  50                printk(KERN_INFO PREFIX "ACPI bus type %s unregistered\n",
  51                       type->bus->name);
  52                return 0;
  53        }
  54        return -ENODEV;
  55}
  56
  57static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
  58{
  59        struct acpi_bus_type *tmp, *ret = NULL;
  60
  61        down_read(&bus_type_sem);
  62        list_for_each_entry(tmp, &bus_type_list, list) {
  63                if (tmp->bus == type) {
  64                        ret = tmp;
  65                        break;
  66                }
  67        }
  68        up_read(&bus_type_sem);
  69        return ret;
  70}
  71
  72static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
  73{
  74        struct acpi_bus_type *tmp;
  75        int ret = -ENODEV;
  76
  77        down_read(&bus_type_sem);
  78        list_for_each_entry(tmp, &bus_type_list, list) {
  79                if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) {
  80                        ret = 0;
  81                        break;
  82                }
  83        }
  84        up_read(&bus_type_sem);
  85        return ret;
  86}
  87
  88/* Get device's handler per its address under its parent */
  89struct acpi_find_child {
  90        acpi_handle handle;
  91        u64 address;
  92};
  93
  94static acpi_status
  95do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv)
  96{
  97        acpi_status status;
  98        struct acpi_device_info *info;
  99        struct acpi_find_child *find = context;
 100
 101        status = acpi_get_object_info(handle, &info);
 102        if (ACPI_SUCCESS(status)) {
 103                if ((info->address == find->address)
 104                        && (info->valid & ACPI_VALID_ADR))
 105                        find->handle = handle;
 106                kfree(info);
 107        }
 108        return AE_OK;
 109}
 110
 111acpi_handle acpi_get_child(acpi_handle parent, u64 address)
 112{
 113        struct acpi_find_child find = { NULL, address };
 114
 115        if (!parent)
 116                return NULL;
 117        acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
 118                            1, do_acpi_find_child, NULL, &find, NULL);
 119        return find.handle;
 120}
 121
 122EXPORT_SYMBOL(acpi_get_child);
 123
 124/* Link ACPI devices with physical devices */
 125static void acpi_glue_data_handler(acpi_handle handle,
 126                                   void *context)
 127{
 128        /* we provide an empty handler */
 129}
 130
 131/* Note: a success call will increase reference count by one */
 132struct device *acpi_get_physical_device(acpi_handle handle)
 133{
 134        acpi_status status;
 135        struct device *dev;
 136
 137        status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev);
 138        if (ACPI_SUCCESS(status))
 139                return get_device(dev);
 140        return NULL;
 141}
 142
 143EXPORT_SYMBOL(acpi_get_physical_device);
 144
 145static int acpi_bind_one(struct device *dev, acpi_handle handle)
 146{
 147        struct acpi_device *acpi_dev;
 148        acpi_status status;
 149
 150        if (dev->archdata.acpi_handle) {
 151                dev_warn(dev, "Drivers changed 'acpi_handle'\n");
 152                return -EINVAL;
 153        }
 154        get_device(dev);
 155        status = acpi_attach_data(handle, acpi_glue_data_handler, dev);
 156        if (ACPI_FAILURE(status)) {
 157                put_device(dev);
 158                return -EINVAL;
 159        }
 160        dev->archdata.acpi_handle = handle;
 161
 162        status = acpi_bus_get_device(handle, &acpi_dev);
 163        if (!ACPI_FAILURE(status)) {
 164                int ret;
 165
 166                ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
 167                                "firmware_node");
 168                ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
 169                                "physical_node");
 170                if (acpi_dev->wakeup.flags.valid)
 171                        device_set_wakeup_capable(dev, true);
 172        }
 173
 174        return 0;
 175}
 176
 177static int acpi_unbind_one(struct device *dev)
 178{
 179        if (!dev->archdata.acpi_handle)
 180                return 0;
 181        if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
 182                struct acpi_device *acpi_dev;
 183
 184                /* acpi_get_physical_device increase refcnt by one */
 185                put_device(dev);
 186
 187                if (!acpi_bus_get_device(dev->archdata.acpi_handle,
 188                                        &acpi_dev)) {
 189                        sysfs_remove_link(&dev->kobj, "firmware_node");
 190                        sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node");
 191                }
 192
 193                acpi_detach_data(dev->archdata.acpi_handle,
 194                                 acpi_glue_data_handler);
 195                dev->archdata.acpi_handle = NULL;
 196                /* acpi_bind_one increase refcnt by one */
 197                put_device(dev);
 198        } else {
 199                dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
 200        }
 201        return 0;
 202}
 203
 204static int acpi_platform_notify(struct device *dev)
 205{
 206        struct acpi_bus_type *type;
 207        acpi_handle handle;
 208        int ret = -EINVAL;
 209
 210        if (!dev->bus || !dev->parent) {
 211                /* bridge devices genernally haven't bus or parent */
 212                ret = acpi_find_bridge_device(dev, &handle);
 213                goto end;
 214        }
 215        type = acpi_get_bus_type(dev->bus);
 216        if (!type) {
 217                DBG("No ACPI bus support for %s\n", dev_name(dev));
 218                ret = -EINVAL;
 219                goto end;
 220        }
 221        if ((ret = type->find_device(dev, &handle)) != 0)
 222                DBG("Can't get handler for %s\n", dev_name(dev));
 223      end:
 224        if (!ret)
 225                acpi_bind_one(dev, handle);
 226
 227#if ACPI_GLUE_DEBUG
 228        if (!ret) {
 229                struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 230
 231                acpi_get_name(dev->archdata.acpi_handle,
 232                              ACPI_FULL_PATHNAME, &buffer);
 233                DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
 234                kfree(buffer.pointer);
 235        } else
 236                DBG("Device %s -> No ACPI support\n", dev_name(dev));
 237#endif
 238
 239        return ret;
 240}
 241
 242static int acpi_platform_notify_remove(struct device *dev)
 243{
 244        acpi_unbind_one(dev);
 245        return 0;
 246}
 247
 248int __init init_acpi_device_notify(void)
 249{
 250        if (platform_notify || platform_notify_remove) {
 251                printk(KERN_ERR PREFIX "Can't use platform_notify\n");
 252                return 0;
 253        }
 254        platform_notify = acpi_platform_notify;
 255        platform_notify_remove = acpi_platform_notify_remove;
 256        return 0;
 257}
 258