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