linux/drivers/pnp/pnpacpi/core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * pnpacpi -- PnP ACPI driver
   4 *
   5 * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr>
   6 * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com>
   7 */
   8
   9#include <linux/export.h>
  10#include <linux/acpi.h>
  11#include <linux/pnp.h>
  12#include <linux/slab.h>
  13#include <linux/mod_devicetable.h>
  14
  15#include "../base.h"
  16#include "pnpacpi.h"
  17
  18static int num;
  19
  20/*
  21 * Compatible Device IDs
  22 */
  23#define TEST_HEX(c) \
  24        if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \
  25                return 0
  26#define TEST_ALPHA(c) \
  27        if (!('A' <= (c) && (c) <= 'Z')) \
  28                return 0
  29static int __init ispnpidacpi(const char *id)
  30{
  31        TEST_ALPHA(id[0]);
  32        TEST_ALPHA(id[1]);
  33        TEST_ALPHA(id[2]);
  34        TEST_HEX(id[3]);
  35        TEST_HEX(id[4]);
  36        TEST_HEX(id[5]);
  37        TEST_HEX(id[6]);
  38        if (id[7] != '\0')
  39                return 0;
  40        return 1;
  41}
  42
  43static int pnpacpi_get_resources(struct pnp_dev *dev)
  44{
  45        pnp_dbg(&dev->dev, "get resources\n");
  46        return pnpacpi_parse_allocated_resource(dev);
  47}
  48
  49static int pnpacpi_set_resources(struct pnp_dev *dev)
  50{
  51        struct acpi_device *acpi_dev;
  52        acpi_handle handle;
  53        int ret = 0;
  54
  55        pnp_dbg(&dev->dev, "set resources\n");
  56
  57        acpi_dev = ACPI_COMPANION(&dev->dev);
  58        if (!acpi_dev) {
  59                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
  60                return -ENODEV;
  61        }
  62
  63        if (WARN_ON_ONCE(acpi_dev != dev->data))
  64                dev->data = acpi_dev;
  65
  66        handle = acpi_dev->handle;
  67        if (acpi_has_method(handle, METHOD_NAME__SRS)) {
  68                struct acpi_buffer buffer;
  69
  70                ret = pnpacpi_build_resource_template(dev, &buffer);
  71                if (ret)
  72                        return ret;
  73
  74                ret = pnpacpi_encode_resources(dev, &buffer);
  75                if (!ret) {
  76                        acpi_status status;
  77
  78                        status = acpi_set_current_resources(handle, &buffer);
  79                        if (ACPI_FAILURE(status))
  80                                ret = -EIO;
  81                }
  82                kfree(buffer.pointer);
  83        }
  84        if (!ret && acpi_device_power_manageable(acpi_dev))
  85                ret = acpi_device_set_power(acpi_dev, ACPI_STATE_D0);
  86
  87        return ret;
  88}
  89
  90static int pnpacpi_disable_resources(struct pnp_dev *dev)
  91{
  92        struct acpi_device *acpi_dev;
  93        acpi_status status;
  94
  95        dev_dbg(&dev->dev, "disable resources\n");
  96
  97        acpi_dev = ACPI_COMPANION(&dev->dev);
  98        if (!acpi_dev) {
  99                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 100                return 0;
 101        }
 102
 103        /* acpi_unregister_gsi(pnp_irq(dev, 0)); */
 104        if (acpi_device_power_manageable(acpi_dev))
 105                acpi_device_set_power(acpi_dev, ACPI_STATE_D3_COLD);
 106
 107        /* continue even if acpi_device_set_power() fails */
 108        status = acpi_evaluate_object(acpi_dev->handle, "_DIS", NULL, NULL);
 109        if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
 110                return -ENODEV;
 111
 112        return 0;
 113}
 114
 115#ifdef CONFIG_ACPI_SLEEP
 116static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
 117{
 118        struct acpi_device *acpi_dev = ACPI_COMPANION(&dev->dev);
 119
 120        if (!acpi_dev) {
 121                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 122                return false;
 123        }
 124
 125        return acpi_bus_can_wakeup(acpi_dev->handle);
 126}
 127
 128static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 129{
 130        struct acpi_device *acpi_dev = ACPI_COMPANION(&dev->dev);
 131        int error = 0;
 132
 133        if (!acpi_dev) {
 134                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 135                return 0;
 136        }
 137
 138        if (device_can_wakeup(&dev->dev)) {
 139                error = acpi_pm_set_device_wakeup(&dev->dev,
 140                                              device_may_wakeup(&dev->dev));
 141                if (error)
 142                        return error;
 143        }
 144
 145        if (acpi_device_power_manageable(acpi_dev)) {
 146                int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL,
 147                                                        ACPI_STATE_D3_COLD);
 148                if (power_state < 0)
 149                        power_state = (state.event == PM_EVENT_ON) ?
 150                                        ACPI_STATE_D0 : ACPI_STATE_D3_COLD;
 151
 152                /*
 153                 * acpi_device_set_power() can fail (keyboard port can't be
 154                 * powered-down?), and in any case, our return value is ignored
 155                 * by pnp_bus_suspend().  Hence we don't revert the wakeup
 156                 * setting if the set_power fails.
 157                 */
 158                error = acpi_device_set_power(acpi_dev, power_state);
 159        }
 160
 161        return error;
 162}
 163
 164static int pnpacpi_resume(struct pnp_dev *dev)
 165{
 166        struct acpi_device *acpi_dev = ACPI_COMPANION(&dev->dev);
 167        int error = 0;
 168
 169        if (!acpi_dev) {
 170                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 171                return -ENODEV;
 172        }
 173
 174        if (device_may_wakeup(&dev->dev))
 175                acpi_pm_set_device_wakeup(&dev->dev, false);
 176
 177        if (acpi_device_power_manageable(acpi_dev))
 178                error = acpi_device_set_power(acpi_dev, ACPI_STATE_D0);
 179
 180        return error;
 181}
 182#endif
 183
 184struct pnp_protocol pnpacpi_protocol = {
 185        .name    = "Plug and Play ACPI",
 186        .get     = pnpacpi_get_resources,
 187        .set     = pnpacpi_set_resources,
 188        .disable = pnpacpi_disable_resources,
 189#ifdef CONFIG_ACPI_SLEEP
 190        .can_wakeup = pnpacpi_can_wakeup,
 191        .suspend = pnpacpi_suspend,
 192        .resume = pnpacpi_resume,
 193#endif
 194};
 195EXPORT_SYMBOL(pnpacpi_protocol);
 196
 197static const char *__init pnpacpi_get_id(struct acpi_device *device)
 198{
 199        struct acpi_hardware_id *id;
 200
 201        list_for_each_entry(id, &device->pnp.ids, list) {
 202                if (ispnpidacpi(id->id))
 203                        return id->id;
 204        }
 205
 206        return NULL;
 207}
 208
 209static int __init pnpacpi_add_device(struct acpi_device *device)
 210{
 211        struct pnp_dev *dev;
 212        const char *pnpid;
 213        struct acpi_hardware_id *id;
 214        int error;
 215
 216        /* Skip devices that are already bound */
 217        if (device->physical_node_count)
 218                return 0;
 219
 220        /*
 221         * If a PnPacpi device is not present , the device
 222         * driver should not be loaded.
 223         */
 224        if (!acpi_has_method(device->handle, "_CRS"))
 225                return 0;
 226
 227        pnpid = pnpacpi_get_id(device);
 228        if (!pnpid)
 229                return 0;
 230
 231        if (!device->status.present)
 232                return 0;
 233
 234        dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid);
 235        if (!dev)
 236                return -ENOMEM;
 237
 238        ACPI_COMPANION_SET(&dev->dev, device);
 239        dev->data = device;
 240        /* .enabled means the device can decode the resources */
 241        dev->active = device->status.enabled;
 242        if (acpi_has_method(device->handle, "_SRS"))
 243                dev->capabilities |= PNP_CONFIGURABLE;
 244        dev->capabilities |= PNP_READ;
 245        if (device->flags.dynamic_status && (dev->capabilities & PNP_CONFIGURABLE))
 246                dev->capabilities |= PNP_WRITE;
 247        if (device->flags.removable)
 248                dev->capabilities |= PNP_REMOVABLE;
 249        if (acpi_has_method(device->handle, "_DIS"))
 250                dev->capabilities |= PNP_DISABLE;
 251
 252        if (strlen(acpi_device_name(device)))
 253                strncpy(dev->name, acpi_device_name(device), sizeof(dev->name));
 254        else
 255                strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name));
 256
 257        if (dev->active)
 258                pnpacpi_parse_allocated_resource(dev);
 259
 260        if (dev->capabilities & PNP_CONFIGURABLE)
 261                pnpacpi_parse_resource_option_data(dev);
 262
 263        list_for_each_entry(id, &device->pnp.ids, list) {
 264                if (!strcmp(id->id, pnpid))
 265                        continue;
 266                if (!ispnpidacpi(id->id))
 267                        continue;
 268                pnp_add_id(dev, id->id);
 269        }
 270
 271        /* clear out the damaged flags */
 272        if (!dev->active)
 273                pnp_init_resources(dev);
 274
 275        error = pnp_add_device(dev);
 276        if (error) {
 277                put_device(&dev->dev);
 278                return error;
 279        }
 280
 281        num++;
 282
 283        return 0;
 284}
 285
 286static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
 287                                                     u32 lvl, void *context,
 288                                                     void **rv)
 289{
 290        struct acpi_device *device;
 291
 292        if (acpi_bus_get_device(handle, &device))
 293                return AE_CTRL_DEPTH;
 294        if (acpi_is_pnp_device(device))
 295                pnpacpi_add_device(device);
 296        return AE_OK;
 297}
 298
 299int pnpacpi_disabled __initdata;
 300static int __init pnpacpi_init(void)
 301{
 302        if (acpi_disabled || pnpacpi_disabled) {
 303                printk(KERN_INFO "pnp: PnP ACPI: disabled\n");
 304                return 0;
 305        }
 306        printk(KERN_INFO "pnp: PnP ACPI init\n");
 307        pnp_register_protocol(&pnpacpi_protocol);
 308        acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL);
 309        printk(KERN_INFO "pnp: PnP ACPI: found %d devices\n", num);
 310        pnp_platform_devices = 1;
 311        return 0;
 312}
 313
 314fs_initcall(pnpacpi_init);
 315
 316static int __init pnpacpi_setup(char *str)
 317{
 318        if (str == NULL)
 319                return 1;
 320        if (!strncmp(str, "off", 3))
 321                pnpacpi_disabled = 1;
 322        return 1;
 323}
 324
 325__setup("pnpacpi=", pnpacpi_setup);
 326