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#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10)
  35
  36int register_acpi_bus_type(struct acpi_bus_type *type)
  37{
  38        if (acpi_disabled)
  39                return -ENODEV;
  40        if (type && type->match && type->find_device) {
  41                down_write(&bus_type_sem);
  42                list_add_tail(&type->list, &bus_type_list);
  43                up_write(&bus_type_sem);
  44                printk(KERN_INFO PREFIX "bus type %s registered\n", type->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 "bus type %s unregistered\n",
  60                       type->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 device *dev)
  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->match(dev)) {
  74                        ret = tmp;
  75                        break;
  76                }
  77        }
  78        up_read(&bus_type_sem);
  79        return ret;
  80}
  81
  82static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used,
  83                                  void *not_used, void **ret_p)
  84{
  85        struct acpi_device *adev = NULL;
  86
  87        acpi_bus_get_device(handle, &adev);
  88        if (adev) {
  89                *ret_p = handle;
  90                return AE_CTRL_TERMINATE;
  91        }
  92        return AE_OK;
  93}
  94
  95static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge)
  96{
  97        unsigned long long sta;
  98        acpi_status status;
  99
 100        status = acpi_bus_get_status_handle(handle, &sta);
 101        if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
 102                return false;
 103
 104        if (is_bridge) {
 105                void *test = NULL;
 106
 107                /* Check if this object has at least one child device. */
 108                acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
 109                                    acpi_dev_present, NULL, NULL, &test);
 110                return !!test;
 111        }
 112        return true;
 113}
 114
 115struct find_child_context {
 116        u64 addr;
 117        bool is_bridge;
 118        acpi_handle ret;
 119        bool ret_checked;
 120};
 121
 122static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
 123                                 void *data, void **not_used)
 124{
 125        struct find_child_context *context = data;
 126        unsigned long long addr;
 127        acpi_status status;
 128
 129        status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
 130        if (ACPI_FAILURE(status) || addr != context->addr)
 131                return AE_OK;
 132
 133        if (!context->ret) {
 134                /* This is the first matching object.  Save its handle. */
 135                context->ret = handle;
 136                return AE_OK;
 137        }
 138        /*
 139         * There is more than one matching object with the same _ADR value.
 140         * That really is unexpected, so we are kind of beyond the scope of the
 141         * spec here.  We have to choose which one to return, though.
 142         *
 143         * First, check if the previously found object is good enough and return
 144         * its handle if so.  Second, check the same for the object that we've
 145         * just found.
 146         */
 147        if (!context->ret_checked) {
 148                if (acpi_extra_checks_passed(context->ret, context->is_bridge))
 149                        return AE_CTRL_TERMINATE;
 150                else
 151                        context->ret_checked = true;
 152        }
 153        if (acpi_extra_checks_passed(handle, context->is_bridge)) {
 154                context->ret = handle;
 155                return AE_CTRL_TERMINATE;
 156        }
 157        return AE_OK;
 158}
 159
 160acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge)
 161{
 162        if (parent) {
 163                struct find_child_context context = {
 164                        .addr = addr,
 165                        .is_bridge = is_bridge,
 166                };
 167
 168                acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child,
 169                                    NULL, &context, NULL);
 170                return context.ret;
 171        }
 172        return NULL;
 173}
 174EXPORT_SYMBOL_GPL(acpi_find_child);
 175
 176int acpi_bind_one(struct device *dev, acpi_handle handle)
 177{
 178        struct acpi_device *acpi_dev;
 179        acpi_status status;
 180        struct acpi_device_physical_node *physical_node, *pn;
 181        char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
 182        struct list_head *physnode_list;
 183        unsigned int node_id;
 184        int retval = -EINVAL;
 185
 186        if (ACPI_HANDLE(dev)) {
 187                if (handle) {
 188                        dev_warn(dev, "ACPI handle is already set\n");
 189                        return -EINVAL;
 190                } else {
 191                        handle = ACPI_HANDLE(dev);
 192                }
 193        }
 194        if (!handle)
 195                return -EINVAL;
 196
 197        get_device(dev);
 198        status = acpi_bus_get_device(handle, &acpi_dev);
 199        if (ACPI_FAILURE(status))
 200                goto err;
 201
 202        physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
 203        if (!physical_node) {
 204                retval = -ENOMEM;
 205                goto err;
 206        }
 207
 208        mutex_lock(&acpi_dev->physical_node_lock);
 209
 210        /*
 211         * Keep the list sorted by node_id so that the IDs of removed nodes can
 212         * be recycled easily.
 213         */
 214        physnode_list = &acpi_dev->physical_node_list;
 215        node_id = 0;
 216        list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {
 217                /* Sanity check. */
 218                if (pn->dev == dev) {
 219                        dev_warn(dev, "Already associated with ACPI node\n");
 220                        goto err_free;
 221                }
 222                if (pn->node_id == node_id) {
 223                        physnode_list = &pn->node;
 224                        node_id++;
 225                }
 226        }
 227
 228        physical_node->node_id = node_id;
 229        physical_node->dev = dev;
 230        list_add(&physical_node->node, physnode_list);
 231        acpi_dev->physical_node_count++;
 232
 233        mutex_unlock(&acpi_dev->physical_node_lock);
 234
 235        if (!ACPI_HANDLE(dev))
 236                ACPI_HANDLE_SET(dev, acpi_dev->handle);
 237
 238        if (!physical_node->node_id)
 239                strcpy(physical_node_name, PHYSICAL_NODE_STRING);
 240        else
 241                sprintf(physical_node_name,
 242                        "physical_node%d", physical_node->node_id);
 243        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
 244                        physical_node_name);
 245        retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
 246                "firmware_node");
 247
 248        if (acpi_dev->wakeup.flags.valid)
 249                device_set_wakeup_capable(dev, true);
 250
 251        return 0;
 252
 253 err:
 254        ACPI_HANDLE_SET(dev, NULL);
 255        put_device(dev);
 256        return retval;
 257
 258 err_free:
 259        mutex_unlock(&acpi_dev->physical_node_lock);
 260        kfree(physical_node);
 261        goto err;
 262}
 263EXPORT_SYMBOL_GPL(acpi_bind_one);
 264
 265int acpi_unbind_one(struct device *dev)
 266{
 267        struct acpi_device_physical_node *entry;
 268        struct acpi_device *acpi_dev;
 269        acpi_status status;
 270        struct list_head *node, *next;
 271
 272        if (!ACPI_HANDLE(dev))
 273                return 0;
 274
 275        status = acpi_bus_get_device(ACPI_HANDLE(dev), &acpi_dev);
 276        if (ACPI_FAILURE(status))
 277                goto err;
 278
 279        mutex_lock(&acpi_dev->physical_node_lock);
 280        list_for_each_safe(node, next, &acpi_dev->physical_node_list) {
 281                char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
 282
 283                entry = list_entry(node, struct acpi_device_physical_node,
 284                        node);
 285                if (entry->dev != dev)
 286                        continue;
 287
 288                list_del(node);
 289
 290                acpi_dev->physical_node_count--;
 291
 292                if (!entry->node_id)
 293                        strcpy(physical_node_name, PHYSICAL_NODE_STRING);
 294                else
 295                        sprintf(physical_node_name,
 296                                "physical_node%d", entry->node_id);
 297
 298                sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name);
 299                sysfs_remove_link(&dev->kobj, "firmware_node");
 300                ACPI_HANDLE_SET(dev, NULL);
 301                /* acpi_bind_one increase refcnt by one */
 302                put_device(dev);
 303                kfree(entry);
 304        }
 305        mutex_unlock(&acpi_dev->physical_node_lock);
 306
 307        return 0;
 308
 309err:
 310        dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
 311        return -EINVAL;
 312}
 313EXPORT_SYMBOL_GPL(acpi_unbind_one);
 314
 315static int acpi_platform_notify(struct device *dev)
 316{
 317        struct acpi_bus_type *type = acpi_get_bus_type(dev);
 318        acpi_handle handle;
 319        int ret;
 320
 321        ret = acpi_bind_one(dev, NULL);
 322        if (ret && type) {
 323                ret = type->find_device(dev, &handle);
 324                if (ret) {
 325                        DBG("Unable to get handle for %s\n", dev_name(dev));
 326                        goto out;
 327                }
 328                ret = acpi_bind_one(dev, handle);
 329                if (ret)
 330                        goto out;
 331        }
 332
 333        if (type && type->setup)
 334                type->setup(dev);
 335
 336 out:
 337#if ACPI_GLUE_DEBUG
 338        if (!ret) {
 339                struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 340
 341                acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer);
 342                DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
 343                kfree(buffer.pointer);
 344        } else
 345                DBG("Device %s -> No ACPI support\n", dev_name(dev));
 346#endif
 347
 348        return ret;
 349}
 350
 351static int acpi_platform_notify_remove(struct device *dev)
 352{
 353        struct acpi_bus_type *type;
 354
 355        type = acpi_get_bus_type(dev);
 356        if (type && type->cleanup)
 357                type->cleanup(dev);
 358
 359        acpi_unbind_one(dev);
 360        return 0;
 361}
 362
 363int __init init_acpi_device_notify(void)
 364{
 365        if (platform_notify || platform_notify_remove) {
 366                printk(KERN_ERR PREFIX "Can't use platform_notify\n");
 367                return 0;
 368        }
 369        platform_notify = acpi_platform_notify;
 370        platform_notify_remove = acpi_platform_notify_remove;
 371        return 0;
 372}
 373
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.