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