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