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
  10#include <linux/acpi_iort.h>
  11#include <linux/export.h>
  12#include <linux/init.h>
  13#include <linux/list.h>
  14#include <linux/device.h>
  15#include <linux/slab.h>
  16#include <linux/rwsem.h>
  17#include <linux/acpi.h>
  18#include <linux/dma-mapping.h>
  19#include <linux/platform_device.h>
  20
  21#include "internal.h"
  22
  23#define ACPI_GLUE_DEBUG 0
  24#if ACPI_GLUE_DEBUG
  25#define DBG(fmt, ...)                                           \
  26        printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__)
  27#else
  28#define DBG(fmt, ...)                                           \
  29do {                                                            \
  30        if (0)                                                  \
  31                printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__);   \
  32} while (0)
  33#endif
  34static LIST_HEAD(bus_type_list);
  35static DECLARE_RWSEM(bus_type_sem);
  36
  37#define PHYSICAL_NODE_STRING "physical_node"
  38#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10)
  39
  40int register_acpi_bus_type(struct acpi_bus_type *type)
  41{
  42        if (acpi_disabled)
  43                return -ENODEV;
  44        if (type && type->match && type->find_companion) {
  45                down_write(&bus_type_sem);
  46                list_add_tail(&type->list, &bus_type_list);
  47                up_write(&bus_type_sem);
  48                printk(KERN_INFO PREFIX "bus type %s registered\n", type->name);
  49                return 0;
  50        }
  51        return -ENODEV;
  52}
  53EXPORT_SYMBOL_GPL(register_acpi_bus_type);
  54
  55int unregister_acpi_bus_type(struct acpi_bus_type *type)
  56{
  57        if (acpi_disabled)
  58                return 0;
  59        if (type) {
  60                down_write(&bus_type_sem);
  61                list_del_init(&type->list);
  62                up_write(&bus_type_sem);
  63                printk(KERN_INFO PREFIX "bus type %s unregistered\n",
  64                       type->name);
  65                return 0;
  66        }
  67        return -ENODEV;
  68}
  69EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
  70
  71static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
  72{
  73        struct acpi_bus_type *tmp, *ret = NULL;
  74
  75        down_read(&bus_type_sem);
  76        list_for_each_entry(tmp, &bus_type_list, list) {
  77                if (tmp->match(dev)) {
  78                        ret = tmp;
  79                        break;
  80                }
  81        }
  82        up_read(&bus_type_sem);
  83        return ret;
  84}
  85
  86#define FIND_CHILD_MIN_SCORE    1
  87#define FIND_CHILD_MAX_SCORE    2
  88
  89static int find_child_checks(struct acpi_device *adev, bool check_children)
  90{
  91        bool sta_present = true;
  92        unsigned long long sta;
  93        acpi_status status;
  94
  95        status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
  96        if (status == AE_NOT_FOUND)
  97                sta_present = false;
  98        else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
  99                return -ENODEV;
 100
 101        if (check_children && list_empty(&adev->children))
 102                return -ENODEV;
 103
 104        /*
 105         * If the device has a _HID returning a valid ACPI/PNP device ID, it is
 106         * better to make it look less attractive here, so that the other device
 107         * with the same _ADR value (that may not have a valid device ID) can be
 108         * matched going forward.  [This means a second spec violation in a row,
 109         * so whatever we do here is best effort anyway.]
 110         */
 111        return sta_present && !adev->pnp.type.platform_id ?
 112                        FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
 113}
 114
 115struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
 116                                           u64 address, bool check_children)
 117{
 118        struct acpi_device *adev, *ret = NULL;
 119        int ret_score = 0;
 120
 121        if (!parent)
 122                return NULL;
 123
 124        list_for_each_entry(adev, &parent->children, node) {
 125                unsigned long long addr;
 126                acpi_status status;
 127                int score;
 128
 129                status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR,
 130                                               NULL, &addr);
 131                if (ACPI_FAILURE(status) || addr != address)
 132                        continue;
 133
 134                if (!ret) {
 135                        /* This is the first matching object.  Save it. */
 136                        ret = adev;
 137                        continue;
 138                }
 139                /*
 140                 * There is more than one matching device object with the same
 141                 * _ADR value.  That really is unexpected, so we are kind of
 142                 * beyond the scope of the spec here.  We have to choose which
 143                 * one to return, though.
 144                 *
 145                 * First, check if the previously found object is good enough
 146                 * and return it if so.  Second, do the same for the object that
 147                 * we've just found.
 148                 */
 149                if (!ret_score) {
 150                        ret_score = find_child_checks(ret, check_children);
 151                        if (ret_score == FIND_CHILD_MAX_SCORE)
 152                                return ret;
 153                }
 154                score = find_child_checks(adev, check_children);
 155                if (score == FIND_CHILD_MAX_SCORE) {
 156                        return adev;
 157                } else if (score > ret_score) {
 158                        ret = adev;
 159                        ret_score = score;
 160                }
 161        }
 162        return ret;
 163}
 164EXPORT_SYMBOL_GPL(acpi_find_child_device);
 165
 166static void acpi_physnode_link_name(char *buf, unsigned int node_id)
 167{
 168        if (node_id > 0)
 169                snprintf(buf, PHYSICAL_NODE_NAME_SIZE,
 170                         PHYSICAL_NODE_STRING "%u", node_id);
 171        else
 172                strcpy(buf, PHYSICAL_NODE_STRING);
 173}
 174
 175int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
 176{
 177        struct acpi_device_physical_node *physical_node, *pn;
 178        char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
 179        struct list_head *physnode_list;
 180        unsigned int node_id;
 181        int retval = -EINVAL;
 182
 183        if (has_acpi_companion(dev)) {
 184                if (acpi_dev) {
 185                        dev_warn(dev, "ACPI companion already set\n");
 186                        return -EINVAL;
 187                } else {
 188                        acpi_dev = ACPI_COMPANION(dev);
 189                }
 190        }
 191        if (!acpi_dev)
 192                return -EINVAL;
 193
 194        get_device(&acpi_dev->dev);
 195        get_device(dev);
 196        physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
 197        if (!physical_node) {
 198                retval = -ENOMEM;
 199                goto err;
 200        }
 201
 202        mutex_lock(&acpi_dev->physical_node_lock);
 203
 204        /*
 205         * Keep the list sorted by node_id so that the IDs of removed nodes can
 206         * be recycled easily.
 207         */
 208        physnode_list = &acpi_dev->physical_node_list;
 209        node_id = 0;
 210        list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {
 211                /* Sanity check. */
 212                if (pn->dev == dev) {
 213                        mutex_unlock(&acpi_dev->physical_node_lock);
 214
 215                        dev_warn(dev, "Already associated with ACPI node\n");
 216                        kfree(physical_node);
 217                        if (ACPI_COMPANION(dev) != acpi_dev)
 218                                goto err;
 219
 220                        put_device(dev);
 221                        put_device(&acpi_dev->dev);
 222                        return 0;
 223                }
 224                if (pn->node_id == node_id) {
 225                        physnode_list = &pn->node;
 226                        node_id++;
 227                }
 228        }
 229
 230        physical_node->node_id = node_id;
 231        physical_node->dev = dev;
 232        list_add(&physical_node->node, physnode_list);
 233        acpi_dev->physical_node_count++;
 234
 235        if (!has_acpi_companion(dev))
 236                ACPI_COMPANION_SET(dev, acpi_dev);
 237
 238        acpi_physnode_link_name(physical_node_name, node_id);
 239        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
 240                                   physical_node_name);
 241        if (retval)
 242                dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n",
 243                        physical_node_name, retval);
 244
 245        retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
 246                                   "firmware_node");
 247        if (retval)
 248                dev_err(dev, "Failed to create link firmware_node (%d)\n",
 249                        retval);
 250
 251        mutex_unlock(&acpi_dev->physical_node_lock);
 252
 253        if (acpi_dev->wakeup.flags.valid)
 254                device_set_wakeup_capable(dev, true);
 255
 256        return 0;
 257
 258 err:
 259        ACPI_COMPANION_SET(dev, NULL);
 260        put_device(dev);
 261        put_device(&acpi_dev->dev);
 262        return retval;
 263}
 264EXPORT_SYMBOL_GPL(acpi_bind_one);
 265
 266int acpi_unbind_one(struct device *dev)
 267{
 268        struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
 269        struct acpi_device_physical_node *entry;
 270
 271        if (!acpi_dev)
 272                return 0;
 273
 274        mutex_lock(&acpi_dev->physical_node_lock);
 275
 276        list_for_each_entry(entry, &acpi_dev->physical_node_list, node)
 277                if (entry->dev == dev) {
 278                        char physnode_name[PHYSICAL_NODE_NAME_SIZE];
 279
 280                        list_del(&entry->node);
 281                        acpi_dev->physical_node_count--;
 282
 283                        acpi_physnode_link_name(physnode_name, entry->node_id);
 284                        sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);
 285                        sysfs_remove_link(&dev->kobj, "firmware_node");
 286                        ACPI_COMPANION_SET(dev, NULL);
 287                        /* Drop references taken by acpi_bind_one(). */
 288                        put_device(dev);
 289                        put_device(&acpi_dev->dev);
 290                        kfree(entry);
 291                        break;
 292                }
 293
 294        mutex_unlock(&acpi_dev->physical_node_lock);
 295        return 0;
 296}
 297EXPORT_SYMBOL_GPL(acpi_unbind_one);
 298
 299static int acpi_device_notify(struct device *dev)
 300{
 301        struct acpi_bus_type *type = acpi_get_bus_type(dev);
 302        struct acpi_device *adev;
 303        int ret;
 304
 305        ret = acpi_bind_one(dev, NULL);
 306        if (ret && type) {
 307                struct acpi_device *adev;
 308
 309                adev = type->find_companion(dev);
 310                if (!adev) {
 311                        DBG("Unable to get handle for %s\n", dev_name(dev));
 312                        ret = -ENODEV;
 313                        goto out;
 314                }
 315                ret = acpi_bind_one(dev, adev);
 316                if (ret)
 317                        goto out;
 318        }
 319        adev = ACPI_COMPANION(dev);
 320        if (!adev)
 321                goto out;
 322
 323        if (dev_is_platform(dev))
 324                acpi_configure_pmsi_domain(dev);
 325
 326        if (type && type->setup)
 327                type->setup(dev);
 328        else if (adev->handler && adev->handler->bind)
 329                adev->handler->bind(dev);
 330
 331 out:
 332#if ACPI_GLUE_DEBUG
 333        if (!ret) {
 334                struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 335
 336                acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer);
 337                DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
 338                kfree(buffer.pointer);
 339        } else
 340                DBG("Device %s -> No ACPI support\n", dev_name(dev));
 341#endif
 342
 343        return ret;
 344}
 345
 346static int acpi_device_notify_remove(struct device *dev)
 347{
 348        struct acpi_device *adev = ACPI_COMPANION(dev);
 349        struct acpi_bus_type *type;
 350
 351        if (!adev)
 352                return 0;
 353
 354        type = acpi_get_bus_type(dev);
 355        if (type && type->cleanup)
 356                type->cleanup(dev);
 357        else if (adev->handler && adev->handler->unbind)
 358                adev->handler->unbind(dev);
 359
 360        acpi_unbind_one(dev);
 361        return 0;
 362}
 363
 364int acpi_platform_notify(struct device *dev, enum kobject_action action)
 365{
 366        switch (action) {
 367        case KOBJ_ADD:
 368                acpi_device_notify(dev);
 369                break;
 370        case KOBJ_REMOVE:
 371                acpi_device_notify_remove(dev);
 372                break;
 373        default:
 374                break;
 375        }
 376        return 0;
 377}
 378