linux/drivers/pnp/pnpacpi/core.c
<<
>>
Prefs
   1/*
   2 * pnpacpi -- PnP ACPI driver
   3 *
   4 * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr>
   5 * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License as published by the
   9 * Free Software Foundation; either version 2, or (at your option) any
  10 * later version.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 */
  21
  22#include <linux/export.h>
  23#include <linux/acpi.h>
  24#include <linux/pnp.h>
  25#include <linux/slab.h>
  26#include <linux/mod_devicetable.h>
  27#include <acpi/acpi_bus.h>
  28
  29#include "../base.h"
  30#include "pnpacpi.h"
  31
  32static int num;
  33
  34/* We need only to blacklist devices that have already an acpi driver that
  35 * can't use pnp layer. We don't need to blacklist device that are directly
  36 * used by the kernel (PCI root, ...), as it is harmless and there were
  37 * already present in pnpbios. But there is an exception for devices that
  38 * have irqs (PIC, Timer) because we call acpi_register_gsi.
  39 * Finally, only devices that have a CRS method need to be in this list.
  40 */
  41static struct acpi_device_id excluded_id_list[] __initdata = {
  42        {"PNP0C09", 0},         /* EC */
  43        {"PNP0C0F", 0},         /* Link device */
  44        {"PNP0000", 0},         /* PIC */
  45        {"PNP0100", 0},         /* Timer */
  46        {"", 0},
  47};
  48
  49static inline int __init is_exclusive_device(struct acpi_device *dev)
  50{
  51        return (!acpi_match_device_ids(dev, excluded_id_list));
  52}
  53
  54/*
  55 * Compatible Device IDs
  56 */
  57#define TEST_HEX(c) \
  58        if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \
  59                return 0
  60#define TEST_ALPHA(c) \
  61        if (!('@' <= (c) || (c) <= 'Z')) \
  62                return 0
  63static int __init ispnpidacpi(const char *id)
  64{
  65        TEST_ALPHA(id[0]);
  66        TEST_ALPHA(id[1]);
  67        TEST_ALPHA(id[2]);
  68        TEST_HEX(id[3]);
  69        TEST_HEX(id[4]);
  70        TEST_HEX(id[5]);
  71        TEST_HEX(id[6]);
  72        if (id[7] != '\0')
  73                return 0;
  74        return 1;
  75}
  76
  77static int pnpacpi_get_resources(struct pnp_dev *dev)
  78{
  79        pnp_dbg(&dev->dev, "get resources\n");
  80        return pnpacpi_parse_allocated_resource(dev);
  81}
  82
  83static int pnpacpi_set_resources(struct pnp_dev *dev)
  84{
  85        struct acpi_device *acpi_dev;
  86        acpi_handle handle;
  87        struct acpi_buffer buffer;
  88        int ret;
  89
  90        pnp_dbg(&dev->dev, "set resources\n");
  91
  92        handle = DEVICE_ACPI_HANDLE(&dev->dev);
  93        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
  94                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
  95                return -ENODEV;
  96        }
  97
  98        ret = pnpacpi_build_resource_template(dev, &buffer);
  99        if (ret)
 100                return ret;
 101        ret = pnpacpi_encode_resources(dev, &buffer);
 102        if (ret) {
 103                kfree(buffer.pointer);
 104                return ret;
 105        }
 106        if (ACPI_FAILURE(acpi_set_current_resources(handle, &buffer)))
 107                ret = -EINVAL;
 108        else if (acpi_bus_power_manageable(handle))
 109                ret = acpi_bus_set_power(handle, ACPI_STATE_D0);
 110        kfree(buffer.pointer);
 111        return ret;
 112}
 113
 114static int pnpacpi_disable_resources(struct pnp_dev *dev)
 115{
 116        struct acpi_device *acpi_dev;
 117        acpi_handle handle;
 118        int ret;
 119
 120        dev_dbg(&dev->dev, "disable resources\n");
 121
 122        handle = DEVICE_ACPI_HANDLE(&dev->dev);
 123        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
 124                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 125                return 0;
 126        }
 127
 128        /* acpi_unregister_gsi(pnp_irq(dev, 0)); */
 129        ret = 0;
 130        if (acpi_bus_power_manageable(handle))
 131                acpi_bus_set_power(handle, ACPI_STATE_D3);
 132                /* continue even if acpi_bus_set_power() fails */
 133        if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DIS", NULL, NULL)))
 134                ret = -ENODEV;
 135        return ret;
 136}
 137
 138#ifdef CONFIG_ACPI_SLEEP
 139static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
 140{
 141        struct acpi_device *acpi_dev;
 142        acpi_handle handle;
 143
 144        handle = DEVICE_ACPI_HANDLE(&dev->dev);
 145        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
 146                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 147                return false;
 148        }
 149
 150        return acpi_bus_can_wakeup(handle);
 151}
 152
 153static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 154{
 155        struct acpi_device *acpi_dev;
 156        acpi_handle handle;
 157        int error = 0;
 158
 159        handle = DEVICE_ACPI_HANDLE(&dev->dev);
 160        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
 161                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 162                return 0;
 163        }
 164
 165        if (device_can_wakeup(&dev->dev)) {
 166                error = acpi_pm_device_sleep_wake(&dev->dev,
 167                                device_may_wakeup(&dev->dev));
 168                if (error)
 169                        return error;
 170        }
 171
 172        if (acpi_bus_power_manageable(handle)) {
 173                int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL);
 174
 175                if (power_state < 0)
 176                        power_state = (state.event == PM_EVENT_ON) ?
 177                                        ACPI_STATE_D0 : ACPI_STATE_D3;
 178
 179                /*
 180                 * acpi_bus_set_power() often fails (keyboard port can't be
 181                 * powered-down?), and in any case, our return value is ignored
 182                 * by pnp_bus_suspend().  Hence we don't revert the wakeup
 183                 * setting if the set_power fails.
 184                 */
 185                error = acpi_bus_set_power(handle, power_state);
 186        }
 187
 188        return error;
 189}
 190
 191static int pnpacpi_resume(struct pnp_dev *dev)
 192{
 193        struct acpi_device *acpi_dev;
 194        acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
 195        int error = 0;
 196
 197        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
 198                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 199                return -ENODEV;
 200        }
 201
 202        if (device_may_wakeup(&dev->dev))
 203                acpi_pm_device_sleep_wake(&dev->dev, false);
 204
 205        if (acpi_bus_power_manageable(handle))
 206                error = acpi_bus_set_power(handle, ACPI_STATE_D0);
 207
 208        return error;
 209}
 210#endif
 211
 212struct pnp_protocol pnpacpi_protocol = {
 213        .name    = "Plug and Play ACPI",
 214        .get     = pnpacpi_get_resources,
 215        .set     = pnpacpi_set_resources,
 216        .disable = pnpacpi_disable_resources,
 217#ifdef CONFIG_ACPI_SLEEP
 218        .can_wakeup = pnpacpi_can_wakeup,
 219        .suspend = pnpacpi_suspend,
 220        .resume = pnpacpi_resume,
 221#endif
 222};
 223EXPORT_SYMBOL(pnpacpi_protocol);
 224
 225static char *__init pnpacpi_get_id(struct acpi_device *device)
 226{
 227        struct acpi_hardware_id *id;
 228
 229        list_for_each_entry(id, &device->pnp.ids, list) {
 230                if (ispnpidacpi(id->id))
 231                        return id->id;
 232        }
 233
 234        return NULL;
 235}
 236
 237static int __init pnpacpi_add_device(struct acpi_device *device)
 238{
 239        acpi_handle temp = NULL;
 240        acpi_status status;
 241        struct pnp_dev *dev;
 242        char *pnpid;
 243        struct acpi_hardware_id *id;
 244
 245        /*
 246         * If a PnPacpi device is not present , the device
 247         * driver should not be loaded.
 248         */
 249        status = acpi_get_handle(device->handle, "_CRS", &temp);
 250        if (ACPI_FAILURE(status))
 251                return 0;
 252
 253        pnpid = pnpacpi_get_id(device);
 254        if (!pnpid)
 255                return 0;
 256
 257        if (is_exclusive_device(device) || !device->status.present)
 258                return 0;
 259
 260        dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid);
 261        if (!dev)
 262                return -ENOMEM;
 263
 264        dev->data = device;
 265        /* .enabled means the device can decode the resources */
 266        dev->active = device->status.enabled;
 267        status = acpi_get_handle(device->handle, "_SRS", &temp);
 268        if (ACPI_SUCCESS(status))
 269                dev->capabilities |= PNP_CONFIGURABLE;
 270        dev->capabilities |= PNP_READ;
 271        if (device->flags.dynamic_status && (dev->capabilities & PNP_CONFIGURABLE))
 272                dev->capabilities |= PNP_WRITE;
 273        if (device->flags.removable)
 274                dev->capabilities |= PNP_REMOVABLE;
 275        status = acpi_get_handle(device->handle, "_DIS", &temp);
 276        if (ACPI_SUCCESS(status))
 277                dev->capabilities |= PNP_DISABLE;
 278
 279        if (strlen(acpi_device_name(device)))
 280                strncpy(dev->name, acpi_device_name(device), sizeof(dev->name));
 281        else
 282                strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name));
 283
 284        if (dev->active)
 285                pnpacpi_parse_allocated_resource(dev);
 286
 287        if (dev->capabilities & PNP_CONFIGURABLE)
 288                pnpacpi_parse_resource_option_data(dev);
 289
 290        list_for_each_entry(id, &device->pnp.ids, list) {
 291                if (!strcmp(id->id, pnpid))
 292                        continue;
 293                if (!ispnpidacpi(id->id))
 294                        continue;
 295                pnp_add_id(dev, id->id);
 296        }
 297
 298        /* clear out the damaged flags */
 299        if (!dev->active)
 300                pnp_init_resources(dev);
 301        pnp_add_device(dev);
 302        num++;
 303
 304        return AE_OK;
 305}
 306
 307static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
 308                                                     u32 lvl, void *context,
 309                                                     void **rv)
 310{
 311        struct acpi_device *device;
 312
 313        if (!acpi_bus_get_device(handle, &device))
 314                pnpacpi_add_device(device);
 315        else
 316                return AE_CTRL_DEPTH;
 317        return AE_OK;
 318}
 319
 320static int __init acpi_pnp_match(struct device *dev, void *_pnp)
 321{
 322        struct acpi_device *acpi = to_acpi_device(dev);
 323        struct pnp_dev *pnp = _pnp;
 324        struct device *physical_device;
 325
 326        physical_device = acpi_get_physical_device(acpi->handle);
 327        if (physical_device)
 328                put_device(physical_device);
 329
 330        /* true means it matched */
 331        return !physical_device
 332            && compare_pnp_id(pnp->id, acpi_device_hid(acpi));
 333}
 334
 335static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle)
 336{
 337        struct device *adev;
 338        struct acpi_device *acpi;
 339
 340        adev = bus_find_device(&acpi_bus_type, NULL,
 341                               to_pnp_dev(dev), acpi_pnp_match);
 342        if (!adev)
 343                return -ENODEV;
 344
 345        acpi = to_acpi_device(adev);
 346        *handle = acpi->handle;
 347        put_device(adev);
 348        return 0;
 349}
 350
 351/* complete initialization of a PNPACPI device includes having
 352 * pnpdev->dev.archdata.acpi_handle point to its ACPI sibling.
 353 */
 354static struct acpi_bus_type __initdata acpi_pnp_bus = {
 355        .bus         = &pnp_bus_type,
 356        .find_device = acpi_pnp_find_device,
 357};
 358
 359int pnpacpi_disabled __initdata;
 360static int __init pnpacpi_init(void)
 361{
 362        if (acpi_disabled || pnpacpi_disabled) {
 363                printk(KERN_INFO "pnp: PnP ACPI: disabled\n");
 364                return 0;
 365        }
 366        printk(KERN_INFO "pnp: PnP ACPI init\n");
 367        pnp_register_protocol(&pnpacpi_protocol);
 368        register_acpi_bus_type(&acpi_pnp_bus);
 369        acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL);
 370        printk(KERN_INFO "pnp: PnP ACPI: found %d devices\n", num);
 371        unregister_acpi_bus_type(&acpi_pnp_bus);
 372        pnp_platform_devices = 1;
 373        return 0;
 374}
 375
 376fs_initcall(pnpacpi_init);
 377
 378static int __init pnpacpi_setup(char *str)
 379{
 380        if (str == NULL)
 381                return 1;
 382        if (!strncmp(str, "off", 3))
 383                pnpacpi_disabled = 1;
 384        return 1;
 385}
 386
 387__setup("pnpacpi=", pnpacpi_setup);
 388