]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blame - drivers/pnp/pnpacpi/core.c
UBUNTU: Ubuntu-5.3.0-29.31
[mirror_ubuntu-eoan-kernel.git] / drivers / pnp / pnpacpi / core.c
CommitLineData
660662f8 1// SPDX-License-Identifier: GPL-2.0-or-later
1da177e4
LT
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>
1da177e4 7 */
55955aad 8
26e6e9e5 9#include <linux/export.h>
1da177e4
LT
10#include <linux/acpi.h>
11#include <linux/pnp.h>
5a0e3ad6 12#include <linux/slab.h>
29b71a1c 13#include <linux/mod_devicetable.h>
29b71a1c 14
1692b27b 15#include "../base.h"
1da177e4
LT
16#include "pnpacpi.h"
17
420a0f66 18static int num;
1da177e4 19
1da177e4
LT
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) \
cdc87c5a 27 if (!('A' <= (c) && (c) <= 'Z')) \
1da177e4 28 return 0
620e112c 29static int __init ispnpidacpi(const char *id)
1da177e4
LT
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
59284cb4 43static int pnpacpi_get_resources(struct pnp_dev *dev)
1da177e4 44{
2f53432c 45 pnp_dbg(&dev->dev, "get resources\n");
d152cf5d 46 return pnpacpi_parse_allocated_resource(dev);
1da177e4
LT
47}
48
59284cb4 49static int pnpacpi_set_resources(struct pnp_dev *dev)
1da177e4 50{
cc8e7a35
RW
51 struct acpi_device *acpi_dev;
52 acpi_handle handle;
a8d22396 53 int ret = 0;
1da177e4 54
2f53432c 55 pnp_dbg(&dev->dev, "set resources\n");
cc8e7a35 56
e70dba60
RW
57 acpi_dev = ACPI_COMPANION(&dev->dev);
58 if (!acpi_dev) {
cc8e7a35
RW
59 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
60 return -ENODEV;
61 }
62
a6b5e88c
RW
63 if (WARN_ON_ONCE(acpi_dev != dev->data))
64 dev->data = acpi_dev;
65
e70dba60 66 handle = acpi_dev->handle;
a8d22396
RW
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 }
1da177e4 82 kfree(buffer.pointer);
1da177e4 83 }
e70dba60
RW
84 if (!ret && acpi_device_power_manageable(acpi_dev))
85 ret = acpi_device_set_power(acpi_dev, ACPI_STATE_D0);
a8d22396 86
1da177e4
LT
87 return ret;
88}
89
90static int pnpacpi_disable_resources(struct pnp_dev *dev)
91{
cc8e7a35 92 struct acpi_device *acpi_dev;
a8d22396 93 acpi_status status;
6328a574
WS
94
95 dev_dbg(&dev->dev, "disable resources\n");
55955aad 96
e70dba60
RW
97 acpi_dev = ACPI_COMPANION(&dev->dev);
98 if (!acpi_dev) {
cc8e7a35
RW
99 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
100 return 0;
101 }
102
1da177e4 103 /* acpi_unregister_gsi(pnp_irq(dev, 0)); */
e70dba60
RW
104 if (acpi_device_power_manageable(acpi_dev))
105 acpi_device_set_power(acpi_dev, ACPI_STATE_D3_COLD);
a8d22396 106
e70dba60
RW
107 /* continue even if acpi_device_set_power() fails */
108 status = acpi_evaluate_object(acpi_dev->handle, "_DIS", NULL, NULL);
a8d22396
RW
109 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
110 return -ENODEV;
111
112 return 0;
1da177e4
LT
113}
114
673d5b43 115#ifdef CONFIG_ACPI_SLEEP
b14e033e
AS
116static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
117{
e70dba60 118 struct acpi_device *acpi_dev = ACPI_COMPANION(&dev->dev);
cc8e7a35 119
e70dba60 120 if (!acpi_dev) {
cc8e7a35
RW
121 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
122 return false;
123 }
b14e033e 124
e70dba60 125 return acpi_bus_can_wakeup(acpi_dev->handle);
b14e033e
AS
126}
127
fc30e68e
SL
128static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
129{
e70dba60 130 struct acpi_device *acpi_dev = ACPI_COMPANION(&dev->dev);
cc8e7a35
RW
131 int error = 0;
132
e70dba60 133 if (!acpi_dev) {
cc8e7a35
RW
134 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
135 return 0;
136 }
36e02b62 137
b14e033e 138 if (device_can_wakeup(&dev->dev)) {
4d183d04
RW
139 error = acpi_pm_set_device_wakeup(&dev->dev,
140 device_may_wakeup(&dev->dev));
cc8e7a35
RW
141 if (error)
142 return error;
143 }
144
e70dba60 145 if (acpi_device_power_manageable(acpi_dev)) {
ee85f543 146 int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL,
8ad928d5 147 ACPI_STATE_D3_COLD);
cc8e7a35
RW
148 if (power_state < 0)
149 power_state = (state.event == PM_EVENT_ON) ?
8ad928d5 150 ACPI_STATE_D0 : ACPI_STATE_D3_COLD;
cc8e7a35
RW
151
152 /*
e70dba60 153 * acpi_device_set_power() can fail (keyboard port can't be
cc8e7a35
RW
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 */
e70dba60 158 error = acpi_device_set_power(acpi_dev, power_state);
b14e033e 159 }
cc8e7a35
RW
160
161 return error;
fc30e68e
SL
162}
163
164static int pnpacpi_resume(struct pnp_dev *dev)
165{
e70dba60 166 struct acpi_device *acpi_dev = ACPI_COMPANION(&dev->dev);
cc8e7a35
RW
167 int error = 0;
168
e70dba60 169 if (!acpi_dev) {
cc8e7a35
RW
170 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
171 return -ENODEV;
172 }
c4da6940 173
b14e033e 174 if (device_may_wakeup(&dev->dev))
4d183d04 175 acpi_pm_set_device_wakeup(&dev->dev, false);
cc8e7a35 176
e70dba60
RW
177 if (acpi_device_power_manageable(acpi_dev))
178 error = acpi_device_set_power(acpi_dev, ACPI_STATE_D0);
cc8e7a35
RW
179
180 return error;
fc30e68e 181}
673d5b43 182#endif
fc30e68e 183
9065ce45 184struct pnp_protocol pnpacpi_protocol = {
07d4e9af
BH
185 .name = "Plug and Play ACPI",
186 .get = pnpacpi_get_resources,
187 .set = pnpacpi_set_resources,
1da177e4 188 .disable = pnpacpi_disable_resources,
673d5b43 189#ifdef CONFIG_ACPI_SLEEP
b14e033e 190 .can_wakeup = pnpacpi_can_wakeup,
fc30e68e
SL
191 .suspend = pnpacpi_suspend,
192 .resume = pnpacpi_resume,
673d5b43 193#endif
1da177e4 194};
9065ce45 195EXPORT_SYMBOL(pnpacpi_protocol);
1da177e4 196
844142c3 197static const char *__init pnpacpi_get_id(struct acpi_device *device)
420a0f66
DT
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
1da177e4
LT
209static int __init pnpacpi_add_device(struct acpi_device *device)
210{
1da177e4 211 struct pnp_dev *dev;
844142c3 212 const char *pnpid;
7f47fa6c 213 struct acpi_hardware_id *id;
249135d1 214 int error;
1da177e4 215
29058753
AH
216 /* Skip devices that are already bound */
217 if (device->physical_node_count)
218 return 0;
219
39a0ad87
ZY
220 /*
221 * If a PnPacpi device is not present , the device
222 * driver should not be loaded.
223 */
0e77e2c4 224 if (!acpi_has_method(device->handle, "_CRS"))
420a0f66
DT
225 return 0;
226
227 pnpid = pnpacpi_get_id(device);
228 if (!pnpid)
229 return 0;
230
eec15edb 231 if (!device->status.present)
1da177e4
LT
232 return 0;
233
420a0f66 234 dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid);
bda1e4e5 235 if (!dev)
1da177e4 236 return -ENOMEM;
bda1e4e5 237
2eb1eb02 238 ACPI_COMPANION_SET(&dev->dev, device);
c4da6940 239 dev->data = device;
07d4e9af 240 /* .enabled means the device can decode the resources */
1da177e4 241 dev->active = device->status.enabled;
0e77e2c4 242 if (acpi_has_method(device->handle, "_SRS"))
1da177e4
LT
243 dev->capabilities |= PNP_CONFIGURABLE;
244 dev->capabilities |= PNP_READ;
856608ee 245 if (device->flags.dynamic_status && (dev->capabilities & PNP_CONFIGURABLE))
1da177e4
LT
246 dev->capabilities |= PNP_WRITE;
247 if (device->flags.removable)
248 dev->capabilities |= PNP_REMOVABLE;
0e77e2c4 249 if (acpi_has_method(device->handle, "_DIS"))
1da177e4
LT
250 dev->capabilities |= PNP_DISABLE;
251
1da177e4
LT
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
d152cf5d
BH
257 if (dev->active)
258 pnpacpi_parse_allocated_resource(dev);
1da177e4 259
d152cf5d
BH
260 if (dev->capabilities & PNP_CONFIGURABLE)
261 pnpacpi_parse_resource_option_data(dev);
55955aad 262
7f47fa6c 263 list_for_each_entry(id, &device->pnp.ids, list) {
420a0f66 264 if (!strcmp(id->id, pnpid))
7f47fa6c
BH
265 continue;
266 if (!ispnpidacpi(id->id))
267 continue;
268 pnp_add_id(dev, id->id);
1da177e4
LT
269 }
270
271 /* clear out the damaged flags */
272 if (!dev->active)
f4490002 273 pnp_init_resources(dev);
249135d1
DT
274
275 error = pnp_add_device(dev);
276 if (error) {
277 put_device(&dev->dev);
278 return error;
279 }
280
9dd78466 281 num++;
1da177e4 282
2eb1eb02 283 return 0;
1da177e4
LT
284}
285
286static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
9dd78466
BH
287 u32 lvl, void *context,
288 void **rv)
1da177e4
LT
289{
290 struct acpi_device *device;
291
eec15edb 292 if (acpi_bus_get_device(handle, &device))
1da177e4 293 return AE_CTRL_DEPTH;
eec15edb
ZR
294 if (acpi_is_pnp_device(device))
295 pnpacpi_add_device(device);
1da177e4
LT
296 return AE_OK;
297}
298
299int pnpacpi_disabled __initdata;
b449f63c 300static int __init pnpacpi_init(void)
1da177e4
LT
301{
302 if (acpi_disabled || pnpacpi_disabled) {
c865d2f6 303 printk(KERN_INFO "pnp: PnP ACPI: disabled\n");
1da177e4
LT
304 return 0;
305 }
c865d2f6 306 printk(KERN_INFO "pnp: PnP ACPI init\n");
1da177e4
LT
307 pnp_register_protocol(&pnpacpi_protocol);
308 acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL);
c865d2f6 309 printk(KERN_INFO "pnp: PnP ACPI: found %d devices\n", num);
8f81dd14 310 pnp_platform_devices = 1;
1da177e4
LT
311 return 0;
312}
9dd78466 313
ed458df4 314fs_initcall(pnpacpi_init);
1da177e4
LT
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}
9dd78466 324
1da177e4 325__setup("pnpacpi=", pnpacpi_setup);