]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/pnp/pnpacpi/core.c
ACPI / PNP: use device ID list for PNPACPI device enumeration
[mirror_ubuntu-artful-kernel.git] / drivers / pnp / pnpacpi / core.c
CommitLineData
1da177e4
LT
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>
55955aad 6 *
1da177e4
LT
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 */
55955aad 21
26e6e9e5 22#include <linux/export.h>
1da177e4
LT
23#include <linux/acpi.h>
24#include <linux/pnp.h>
5a0e3ad6 25#include <linux/slab.h>
29b71a1c 26#include <linux/mod_devicetable.h>
29b71a1c 27
1692b27b 28#include "../base.h"
1da177e4
LT
29#include "pnpacpi.h"
30
420a0f66 31static int num;
1da177e4 32
1da177e4
LT
33/*
34 * Compatible Device IDs
35 */
36#define TEST_HEX(c) \
37 if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \
38 return 0
39#define TEST_ALPHA(c) \
cdc87c5a 40 if (!('A' <= (c) && (c) <= 'Z')) \
1da177e4 41 return 0
620e112c 42static int __init ispnpidacpi(const char *id)
1da177e4
LT
43{
44 TEST_ALPHA(id[0]);
45 TEST_ALPHA(id[1]);
46 TEST_ALPHA(id[2]);
47 TEST_HEX(id[3]);
48 TEST_HEX(id[4]);
49 TEST_HEX(id[5]);
50 TEST_HEX(id[6]);
51 if (id[7] != '\0')
52 return 0;
53 return 1;
54}
55
59284cb4 56static int pnpacpi_get_resources(struct pnp_dev *dev)
1da177e4 57{
2f53432c 58 pnp_dbg(&dev->dev, "get resources\n");
d152cf5d 59 return pnpacpi_parse_allocated_resource(dev);
1da177e4
LT
60}
61
59284cb4 62static int pnpacpi_set_resources(struct pnp_dev *dev)
1da177e4 63{
cc8e7a35
RW
64 struct acpi_device *acpi_dev;
65 acpi_handle handle;
a8d22396 66 int ret = 0;
1da177e4 67
2f53432c 68 pnp_dbg(&dev->dev, "set resources\n");
cc8e7a35 69
3a83f992 70 handle = ACPI_HANDLE(&dev->dev);
cfc57557 71 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
cc8e7a35
RW
72 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
73 return -ENODEV;
74 }
75
a6b5e88c
RW
76 if (WARN_ON_ONCE(acpi_dev != dev->data))
77 dev->data = acpi_dev;
78
a8d22396
RW
79 if (acpi_has_method(handle, METHOD_NAME__SRS)) {
80 struct acpi_buffer buffer;
81
82 ret = pnpacpi_build_resource_template(dev, &buffer);
83 if (ret)
84 return ret;
85
86 ret = pnpacpi_encode_resources(dev, &buffer);
87 if (!ret) {
88 acpi_status status;
89
90 status = acpi_set_current_resources(handle, &buffer);
91 if (ACPI_FAILURE(status))
92 ret = -EIO;
93 }
1da177e4 94 kfree(buffer.pointer);
1da177e4 95 }
a8d22396 96 if (!ret && acpi_bus_power_manageable(handle))
6328a574 97 ret = acpi_bus_set_power(handle, ACPI_STATE_D0);
a8d22396 98
1da177e4
LT
99 return ret;
100}
101
102static int pnpacpi_disable_resources(struct pnp_dev *dev)
103{
cc8e7a35
RW
104 struct acpi_device *acpi_dev;
105 acpi_handle handle;
a8d22396 106 acpi_status status;
6328a574
WS
107
108 dev_dbg(&dev->dev, "disable resources\n");
55955aad 109
3a83f992 110 handle = ACPI_HANDLE(&dev->dev);
cfc57557 111 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
cc8e7a35
RW
112 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
113 return 0;
114 }
115
1da177e4 116 /* acpi_unregister_gsi(pnp_irq(dev, 0)); */
19bde778 117 if (acpi_bus_power_manageable(handle))
8ad928d5 118 acpi_bus_set_power(handle, ACPI_STATE_D3_COLD);
a8d22396
RW
119
120 /* continue even if acpi_bus_set_power() fails */
121 status = acpi_evaluate_object(handle, "_DIS", NULL, NULL);
122 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
123 return -ENODEV;
124
125 return 0;
1da177e4
LT
126}
127
673d5b43 128#ifdef CONFIG_ACPI_SLEEP
b14e033e
AS
129static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
130{
cc8e7a35
RW
131 struct acpi_device *acpi_dev;
132 acpi_handle handle;
133
3a83f992 134 handle = ACPI_HANDLE(&dev->dev);
cfc57557 135 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
cc8e7a35
RW
136 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
137 return false;
138 }
b14e033e
AS
139
140 return acpi_bus_can_wakeup(handle);
141}
142
fc30e68e
SL
143static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
144{
cc8e7a35
RW
145 struct acpi_device *acpi_dev;
146 acpi_handle handle;
147 int error = 0;
148
3a83f992 149 handle = ACPI_HANDLE(&dev->dev);
cfc57557 150 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
cc8e7a35
RW
151 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
152 return 0;
153 }
36e02b62 154
b14e033e 155 if (device_can_wakeup(&dev->dev)) {
cc8e7a35 156 error = acpi_pm_device_sleep_wake(&dev->dev,
b14e033e 157 device_may_wakeup(&dev->dev));
cc8e7a35
RW
158 if (error)
159 return error;
160 }
161
162 if (acpi_bus_power_manageable(handle)) {
ee85f543 163 int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL,
8ad928d5 164 ACPI_STATE_D3_COLD);
cc8e7a35
RW
165 if (power_state < 0)
166 power_state = (state.event == PM_EVENT_ON) ?
8ad928d5 167 ACPI_STATE_D0 : ACPI_STATE_D3_COLD;
cc8e7a35
RW
168
169 /*
170 * acpi_bus_set_power() often fails (keyboard port can't be
171 * powered-down?), and in any case, our return value is ignored
172 * by pnp_bus_suspend(). Hence we don't revert the wakeup
173 * setting if the set_power fails.
174 */
175 error = acpi_bus_set_power(handle, power_state);
b14e033e 176 }
cc8e7a35
RW
177
178 return error;
fc30e68e
SL
179}
180
181static int pnpacpi_resume(struct pnp_dev *dev)
182{
cc8e7a35 183 struct acpi_device *acpi_dev;
3a83f992 184 acpi_handle handle = ACPI_HANDLE(&dev->dev);
cc8e7a35
RW
185 int error = 0;
186
cfc57557 187 if (!handle || acpi_bus_get_device(handle, &acpi_dev)) {
cc8e7a35
RW
188 dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
189 return -ENODEV;
190 }
c4da6940 191
b14e033e
AS
192 if (device_may_wakeup(&dev->dev))
193 acpi_pm_device_sleep_wake(&dev->dev, false);
cc8e7a35
RW
194
195 if (acpi_bus_power_manageable(handle))
196 error = acpi_bus_set_power(handle, ACPI_STATE_D0);
197
198 return error;
fc30e68e 199}
673d5b43 200#endif
fc30e68e 201
9065ce45 202struct pnp_protocol pnpacpi_protocol = {
07d4e9af
BH
203 .name = "Plug and Play ACPI",
204 .get = pnpacpi_get_resources,
205 .set = pnpacpi_set_resources,
1da177e4 206 .disable = pnpacpi_disable_resources,
673d5b43 207#ifdef CONFIG_ACPI_SLEEP
b14e033e 208 .can_wakeup = pnpacpi_can_wakeup,
fc30e68e
SL
209 .suspend = pnpacpi_suspend,
210 .resume = pnpacpi_resume,
673d5b43 211#endif
1da177e4 212};
9065ce45 213EXPORT_SYMBOL(pnpacpi_protocol);
1da177e4 214
66c3ec4f 215static char *__init pnpacpi_get_id(struct acpi_device *device)
420a0f66
DT
216{
217 struct acpi_hardware_id *id;
218
219 list_for_each_entry(id, &device->pnp.ids, list) {
220 if (ispnpidacpi(id->id))
221 return id->id;
222 }
223
224 return NULL;
225}
226
1da177e4
LT
227static int __init pnpacpi_add_device(struct acpi_device *device)
228{
1da177e4 229 struct pnp_dev *dev;
420a0f66 230 char *pnpid;
7f47fa6c 231 struct acpi_hardware_id *id;
249135d1 232 int error;
1da177e4 233
29058753
AH
234 /* Skip devices that are already bound */
235 if (device->physical_node_count)
236 return 0;
237
39a0ad87
ZY
238 /*
239 * If a PnPacpi device is not present , the device
240 * driver should not be loaded.
241 */
0e77e2c4 242 if (!acpi_has_method(device->handle, "_CRS"))
420a0f66
DT
243 return 0;
244
245 pnpid = pnpacpi_get_id(device);
246 if (!pnpid)
247 return 0;
248
eec15edb 249 if (!device->status.present)
1da177e4
LT
250 return 0;
251
420a0f66 252 dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid);
bda1e4e5 253 if (!dev)
1da177e4 254 return -ENOMEM;
bda1e4e5 255
c4da6940 256 dev->data = device;
07d4e9af 257 /* .enabled means the device can decode the resources */
1da177e4 258 dev->active = device->status.enabled;
0e77e2c4 259 if (acpi_has_method(device->handle, "_SRS"))
1da177e4
LT
260 dev->capabilities |= PNP_CONFIGURABLE;
261 dev->capabilities |= PNP_READ;
856608ee 262 if (device->flags.dynamic_status && (dev->capabilities & PNP_CONFIGURABLE))
1da177e4
LT
263 dev->capabilities |= PNP_WRITE;
264 if (device->flags.removable)
265 dev->capabilities |= PNP_REMOVABLE;
0e77e2c4 266 if (acpi_has_method(device->handle, "_DIS"))
1da177e4
LT
267 dev->capabilities |= PNP_DISABLE;
268
1da177e4
LT
269 if (strlen(acpi_device_name(device)))
270 strncpy(dev->name, acpi_device_name(device), sizeof(dev->name));
271 else
272 strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name));
273
d152cf5d
BH
274 if (dev->active)
275 pnpacpi_parse_allocated_resource(dev);
1da177e4 276
d152cf5d
BH
277 if (dev->capabilities & PNP_CONFIGURABLE)
278 pnpacpi_parse_resource_option_data(dev);
55955aad 279
7f47fa6c 280 list_for_each_entry(id, &device->pnp.ids, list) {
420a0f66 281 if (!strcmp(id->id, pnpid))
7f47fa6c
BH
282 continue;
283 if (!ispnpidacpi(id->id))
284 continue;
285 pnp_add_id(dev, id->id);
1da177e4
LT
286 }
287
288 /* clear out the damaged flags */
289 if (!dev->active)
f4490002 290 pnp_init_resources(dev);
249135d1
DT
291
292 error = pnp_add_device(dev);
293 if (error) {
294 put_device(&dev->dev);
295 return error;
296 }
297
9dd78466 298 num++;
1da177e4 299
249135d1 300 return 0;
1da177e4
LT
301}
302
303static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
9dd78466
BH
304 u32 lvl, void *context,
305 void **rv)
1da177e4
LT
306{
307 struct acpi_device *device;
308
eec15edb 309 if (acpi_bus_get_device(handle, &device))
1da177e4 310 return AE_CTRL_DEPTH;
eec15edb
ZR
311 if (acpi_is_pnp_device(device))
312 pnpacpi_add_device(device);
1da177e4
LT
313 return AE_OK;
314}
315
55955aad
DB
316static int __init acpi_pnp_match(struct device *dev, void *_pnp)
317{
9dd78466
BH
318 struct acpi_device *acpi = to_acpi_device(dev);
319 struct pnp_dev *pnp = _pnp;
55955aad
DB
320
321 /* true means it matched */
1033f904 322 return !acpi->physical_node_count
ea8d82fd 323 && compare_pnp_id(pnp->id, acpi_device_hid(acpi));
55955aad
DB
324}
325
e3f02c52 326static struct acpi_device * __init acpi_pnp_find_companion(struct device *dev)
55955aad 327{
e3f02c52
RW
328 dev = bus_find_device(&acpi_bus_type, NULL, to_pnp_dev(dev),
329 acpi_pnp_match);
330 if (!dev)
331 return NULL;
55955aad 332
e3f02c52
RW
333 put_device(dev);
334 return to_acpi_device(dev);
55955aad
DB
335}
336
337/* complete initialization of a PNPACPI device includes having
338 * pnpdev->dev.archdata.acpi_handle point to its ACPI sibling.
339 */
53540098
RW
340static bool acpi_pnp_bus_match(struct device *dev)
341{
342 return dev->bus == &pnp_bus_type;
343}
344
55955aad 345static struct acpi_bus_type __initdata acpi_pnp_bus = {
53540098
RW
346 .name = "PNP",
347 .match = acpi_pnp_bus_match,
e3f02c52 348 .find_companion = acpi_pnp_find_companion,
55955aad
DB
349};
350
1da177e4 351int pnpacpi_disabled __initdata;
b449f63c 352static int __init pnpacpi_init(void)
1da177e4
LT
353{
354 if (acpi_disabled || pnpacpi_disabled) {
c865d2f6 355 printk(KERN_INFO "pnp: PnP ACPI: disabled\n");
1da177e4
LT
356 return 0;
357 }
c865d2f6 358 printk(KERN_INFO "pnp: PnP ACPI init\n");
1da177e4 359 pnp_register_protocol(&pnpacpi_protocol);
55955aad 360 register_acpi_bus_type(&acpi_pnp_bus);
1da177e4 361 acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL);
c865d2f6 362 printk(KERN_INFO "pnp: PnP ACPI: found %d devices\n", num);
55955aad 363 unregister_acpi_bus_type(&acpi_pnp_bus);
8f81dd14 364 pnp_platform_devices = 1;
1da177e4
LT
365 return 0;
366}
9dd78466 367
ed458df4 368fs_initcall(pnpacpi_init);
1da177e4
LT
369
370static int __init pnpacpi_setup(char *str)
371{
372 if (str == NULL)
373 return 1;
374 if (!strncmp(str, "off", 3))
375 pnpacpi_disabled = 1;
376 return 1;
377}
9dd78466 378
1da177e4 379__setup("pnpacpi=", pnpacpi_setup);