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/export.h>
  10#include <linux/init.h>
  11#include <linux/list.h>
  12#include <linux/device.h>
  13#include <linux/slab.h>
  14#include <linux/rwsem.h>
  15#include <linux/acpi.h>
  16
  17#include "internal.h"
  18
  19#define ACPI_GLUE_DEBUG 0
  20#if ACPI_GLUE_DEBUG
  21#define DBG(fmt, ...)                                           \
  22        printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__)
  23#else
  24#define DBG(fmt, ...)                                           \
  25do {                                                            \
  26        if (0)                                                  \
  27                printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__);   \
  28} while (0)
  29#endif
  30static LIST_HEAD(bus_type_list);
  31static DECLARE_RWSEM(bus_type_sem);
  32
  33#define PHYSICAL_NODE_STRING "physical_node"
  34
  35int register_acpi_bus_type(struct acpi_bus_type *type)
  36{
  37        if (acpi_disabled)
  38                return -ENODEV;
  39        if (type && type->match && type->find_device) {
  40                down_write(&bus_type_sem);
  41                list_add_tail(&type->list, &bus_type_list);
  42                up_write(&bus_type_sem);
  43                printk(KERN_INFO PREFIX "bus type %s registered\n", type->name);
  44                return 0;
  45        }
  46        return -ENODEV;
  47}
  48EXPORT_SYMBOL_GPL(register_acpi_bus_type);
  49
  50int unregister_acpi_bus_type(struct acpi_bus_type *type)
  51{
  52        if (acpi_disabled)
  53                return 0;
  54        if (type) {
  55                down_write(&bus_type_sem);
  56                list_del_init(&type->list);
  57                up_write(&bus_type_sem);
  58                printk(KERN_INFO PREFIX "bus type %s unregistered\n",
  59                       type->name);
  60                return 0;
  61        }
  62        return -ENODEV;
  63}
  64EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
  65
  66static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
  67{
  68        struct acpi_bus_type *tmp, *ret = NULL;
  69
  70        down_read(&bus_type_sem);
  71        list_for_each_entry(tmp, &bus_type_list, list) {
  72                if (tmp->match(dev)) {
  73                        ret = tmp;
  74                        break;
  75                }
  76        }
  77        up_read(&bus_type_sem);
  78        return ret;
  79}
  80
  81static acpi_status do_acpi_find_child(acpi_handle handle, u32 lvl_not_used,
  82                                      void *addr_p, void **ret_p)
  83{
  84        unsigned long long addr;
  85        acpi_status status;
  86
  87        status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
  88        if (ACPI_SUCCESS(status) && addr == *((u64 *)addr_p)) {
  89                *ret_p = handle;
  90                return AE_CTRL_TERMINATE;
  91        }
  92        return AE_OK;
  93}
  94
  95acpi_handle acpi_get_child(acpi_handle parent, u64 address)
  96{
  97        void *ret = NULL;
  98
  99        if (!parent)
 100                return NULL;
 101
 102        acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, NULL,
 103                            do_acpi_find_child, &address, &ret);
 104        return (acpi_handle)ret;
 105}
 106EXPORT_SYMBOL(acpi_get_child);
 107
 108static int acpi_bind_one(struct device *dev, acpi_handle handle)
 109{
 110        struct acpi_device *acpi_dev;
 111        acpi_status status;
 112        struct acpi_device_physical_node *physical_node, *pn;
 113        char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2];
 114        int retval = -EINVAL;
 115
 116        if (ACPI_HANDLE(dev)) {
 117                if (handle) {
 118                        dev_warn(dev, "ACPI handle is already set\n");
 119                        return -EINVAL;
 120                } else {
 121                        handle = ACPI_HANDLE(dev);
 122                }
 123        }
 124        if (!handle)
 125                return -EINVAL;
 126
 127        get_device(dev);
 128        status = acpi_bus_get_device(handle, &acpi_dev);
 129        if (ACPI_FAILURE(status))
 130                goto err;
 131
 132        physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
 133        if (!physical_node) {
 134                retval = -ENOMEM;
 135                goto err;
 136        }
 137
 138        mutex_lock(&acpi_dev->physical_node_lock);
 139
 140        /* Sanity check. */
 141        list_for_each_entry(pn, &acpi_dev->physical_node_list, node)
 142                if (pn->dev == dev) {
 143                        dev_warn(dev, "Already associated with ACPI node\n");
 144                        goto err_free;
 145                }
 146
 147        /* allocate physical node id according to physical_node_id_bitmap */
 148        physical_node->node_id =
 149                find_first_zero_bit(acpi_dev->physical_node_id_bitmap,
 150                ACPI_MAX_PHYSICAL_NODE);
 151        if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) {
 152                retval = -ENOSPC;
 153                goto err_free;
 154        }
 155
 156        set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap);
 157        physical_node->dev = dev;
 158        list_add_tail(&physical_node->node, &acpi_dev->physical_node_list);
 159        acpi_dev->physical_node_count++;
 160
 161        mutex_unlock(&acpi_dev->physical_node_lock);
 162
 163        if (!ACPI_HANDLE(dev))
 164                ACPI_HANDLE_SET(dev, acpi_dev->handle);
 165
 166        if (!physical_node->node_id)
 167                strcpy(physical_node_name, PHYSICAL_NODE_STRING);
 168        else
 169                sprintf(physical_node_name,
 170                        "physical_node%d", physical_node->node_id);
 171        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
 172                        physical_node_name);
 173        retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
 174                "firmware_node");
 175
 176        if (acpi_dev->wakeup.flags.valid)
 177                device_set_wakeup_capable(dev, true);
 178
 179        return 0;
 180
 181 err:
 182        ACPI_HANDLE_SET(dev, NULL);
 183        put_device(dev);
 184        return retval;
 185
 186 err_free:
 187        mutex_unlock(&acpi_dev->physical_node_lock);
 188        kfree(physical_node);
 189        goto err;
 190}
 191
 192static int acpi_unbind_one(struct device *dev)
 193{
 194        struct acpi_device_physical_node *entry;
 195        struct acpi_device *acpi_dev;
 196        acpi_status status;
 197        struct list_head *node, *next;
 198
 199        if (!ACPI_HANDLE(dev))
 200                return 0;
 201
 202        status = acpi_bus_get_device(ACPI_HANDLE(dev), &acpi_dev);
 203        if (ACPI_FAILURE(status))
 204                goto err;
 205
 206        mutex_lock(&acpi_dev->physical_node_lock);
 207        list_for_each_safe(node, next, &acpi_dev->physical_node_list) {
 208                char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2];
 209
 210                entry = list_entry(node, struct acpi_device_physical_node,
 211                        node);
 212                if (entry->dev != dev)
 213                        continue;
 214
 215                list_del(node);
 216                clear_bit(entry->node_id, acpi_dev->physical_node_id_bitmap);
 217
 218                acpi_dev->physical_node_count--;
 219
 220                if (!entry->node_id)
 221                        strcpy(physical_node_name, PHYSICAL_NODE_STRING);
 222                else
 223                        sprintf(physical_node_name,
 224                                "physical_node%d", entry->node_id);
 225
 226                sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name);
 227                sysfs_remove_link(&dev->kobj, "firmware_node");
 228                ACPI_HANDLE_SET(dev, NULL);
 229                /* acpi_bind_one increase refcnt by one */
 230                put_device(dev);
 231                kfree(entry);
 232        }
 233        mutex_unlock(&acpi_dev->physical_node_lock);
 234
 235        return 0;
 236
 237err:
 238        dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
 239        return -EINVAL;
 240}
 241
 242static int acpi_platform_notify(struct device *dev)
 243{
 244        struct acpi_bus_type *type = acpi_get_bus_type(dev);
 245        acpi_handle handle;
 246        int ret;
 247
 248        ret = acpi_bind_one(dev, NULL);
 249        if (ret && type) {
 250                ret = type->find_device(dev, &handle);
 251                if (ret) {
 252                        DBG("Unable to get handle for %s\n", dev_name(dev));
 253                        goto out;
 254                }
 255                ret = acpi_bind_one(dev, handle);
 256                if (ret)
 257                        goto out;
 258        }
 259
 260        if (type && type->setup)
 261                type->setup(dev);
 262
 263 out:
 264#if ACPI_GLUE_DEBUG
 265        if (!ret) {
 266                struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 267
 268                acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer);
 269                DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
 270                kfree(buffer.pointer);
 271        } else
 272                DBG("Device %s -> No ACPI support\n", dev_name(dev));
 273#endif
 274
 275        return ret;
 276}
 277
 278static int acpi_platform_notify_remove(struct device *dev)
 279{
 280        struct acpi_bus_type *type;
 281
 282        type = acpi_get_bus_type(dev);
 283        if (type && type->cleanup)
 284                type->cleanup(dev);
 285
 286        acpi_unbind_one(dev);
 287        return 0;
 288}
 289
 290int __init init_acpi_device_notify(void)
 291{
 292        if (platform_notify || platform_notify_remove) {
 293                printk(KERN_ERR PREFIX "Can't use platform_notify\n");
 294                return 0;
 295        }
 296        platform_notify = acpi_platform_notify;
 297        platform_notify_remove = acpi_platform_notify_remove;
 298        return 0;
 299}
 300
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.