xref: /openbmc/linux/drivers/pnp/pnpacpi/core.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
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/acpi.h>
23 #include <linux/pnp.h>
24 #include <acpi/acpi_bus.h>
25 #include "pnpacpi.h"
26 
27 static int num = 0;
28 
29 static char __initdata excluded_id_list[] =
30 	"PNP0C0A," /* Battery */
31 	"PNP0C0C,PNP0C0E,PNP0C0D," /* Button */
32 	"PNP0C09," /* EC */
33 	"PNP0C0B," /* Fan */
34 	"PNP0A03," /* PCI root */
35 	"PNP0C0F," /* Link device */
36 	"PNP0000," /* PIC */
37 	"PNP0100," /* Timer */
38 	;
39 static inline int is_exclusive_device(struct acpi_device *dev)
40 {
41 	return (!acpi_match_ids(dev, excluded_id_list));
42 }
43 
44 void *pnpacpi_kmalloc(size_t size, int f)
45 {
46 	void *p = kmalloc(size, f);
47 	if (p)
48 		memset(p, 0, size);
49 	return p;
50 }
51 
52 /*
53  * Compatible Device IDs
54  */
55 #define TEST_HEX(c) \
56 	if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \
57 		return 0
58 #define TEST_ALPHA(c) \
59 	if (!('@' <= (c) || (c) <= 'Z')) \
60 		return 0
61 static int __init ispnpidacpi(char *id)
62 {
63 	TEST_ALPHA(id[0]);
64 	TEST_ALPHA(id[1]);
65 	TEST_ALPHA(id[2]);
66 	TEST_HEX(id[3]);
67 	TEST_HEX(id[4]);
68 	TEST_HEX(id[5]);
69 	TEST_HEX(id[6]);
70 	if (id[7] != '\0')
71 		return 0;
72 	return 1;
73 }
74 
75 static void __init pnpidacpi_to_pnpid(char *id, char *str)
76 {
77 	str[0] = id[0];
78 	str[1] = id[1];
79 	str[2] = id[2];
80 	str[3] = tolower(id[3]);
81 	str[4] = tolower(id[4]);
82 	str[5] = tolower(id[5]);
83 	str[6] = tolower(id[6]);
84 	str[7] = '\0';
85 }
86 
87 static int pnpacpi_get_resources(struct pnp_dev * dev, struct pnp_resource_table * res)
88 {
89 	acpi_status status;
90 	status = pnpacpi_parse_allocated_resource((acpi_handle)dev->data,
91 		&dev->res);
92 	return ACPI_FAILURE(status) ? -ENODEV : 0;
93 }
94 
95 static int pnpacpi_set_resources(struct pnp_dev * dev, struct pnp_resource_table * res)
96 {
97 	acpi_handle handle = dev->data;
98 	struct acpi_buffer buffer;
99 	int ret = 0;
100 	acpi_status status;
101 
102 	ret = pnpacpi_build_resource_template(handle, &buffer);
103 	if (ret)
104 		return ret;
105 	ret = pnpacpi_encode_resources(res, &buffer);
106 	if (ret) {
107 		kfree(buffer.pointer);
108 		return ret;
109 	}
110 	status = acpi_set_current_resources(handle, &buffer);
111 	if (ACPI_FAILURE(status))
112 		ret = -EINVAL;
113 	kfree(buffer.pointer);
114 	return ret;
115 }
116 
117 static int pnpacpi_disable_resources(struct pnp_dev *dev)
118 {
119 	acpi_status status;
120 
121 	/* acpi_unregister_gsi(pnp_irq(dev, 0)); */
122 	status = acpi_evaluate_object((acpi_handle)dev->data,
123 		"_DIS", NULL, NULL);
124 	return ACPI_FAILURE(status) ? -ENODEV : 0;
125 }
126 
127 struct pnp_protocol pnpacpi_protocol = {
128 	.name	= "Plug and Play ACPI",
129 	.get	= pnpacpi_get_resources,
130 	.set	= pnpacpi_set_resources,
131 	.disable = pnpacpi_disable_resources,
132 };
133 
134 static int __init pnpacpi_add_device(struct acpi_device *device)
135 {
136 	acpi_handle temp = NULL;
137 	acpi_status status;
138 	struct pnp_id *dev_id;
139 	struct pnp_dev *dev;
140 
141 	if (!ispnpidacpi(acpi_device_hid(device)) ||
142 		is_exclusive_device(device))
143 		return 0;
144 
145 	pnp_dbg("ACPI device : hid %s", acpi_device_hid(device));
146 	dev =  pnpacpi_kmalloc(sizeof(struct pnp_dev), GFP_KERNEL);
147 	if (!dev) {
148 		pnp_err("Out of memory");
149 		return -ENOMEM;
150 	}
151 	dev->data = device->handle;
152 	/* .enabled means if the device can decode the resources */
153 	dev->active = device->status.enabled;
154 	status = acpi_get_handle(device->handle, "_SRS", &temp);
155 	if (ACPI_SUCCESS(status))
156 		dev->capabilities |= PNP_CONFIGURABLE;
157 	dev->capabilities |= PNP_READ;
158 	if (device->flags.dynamic_status)
159 		dev->capabilities |= PNP_WRITE;
160 	if (device->flags.removable)
161 		dev->capabilities |= PNP_REMOVABLE;
162 	status = acpi_get_handle(device->handle, "_DIS", &temp);
163 	if (ACPI_SUCCESS(status))
164 		dev->capabilities |= PNP_DISABLE;
165 
166 	dev->protocol = &pnpacpi_protocol;
167 
168 	if (strlen(acpi_device_name(device)))
169 		strncpy(dev->name, acpi_device_name(device), sizeof(dev->name));
170 	else
171 		strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name));
172 
173 	dev->number = num;
174 
175 	/* set the initial values for the PnP device */
176 	dev_id = pnpacpi_kmalloc(sizeof(struct pnp_id), GFP_KERNEL);
177 	if (!dev_id)
178 		goto err;
179 	pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id);
180 	pnp_add_id(dev_id, dev);
181 
182 	if(dev->active) {
183 		/* parse allocated resource */
184 		status = pnpacpi_parse_allocated_resource(device->handle, &dev->res);
185 		if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
186 			pnp_err("PnPACPI: METHOD_NAME__CRS failure for %s", dev_id->id);
187 			goto err1;
188 		}
189 	}
190 
191 	if(dev->capabilities & PNP_CONFIGURABLE) {
192 		status = pnpacpi_parse_resource_option_data(device->handle,
193 			dev);
194 		if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
195 			pnp_err("PnPACPI: METHOD_NAME__PRS failure for %s", dev_id->id);
196 			goto err1;
197 		}
198 	}
199 
200 	/* parse compatible ids */
201 	if (device->flags.compatible_ids) {
202 		struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;
203 		int i;
204 
205 		for (i = 0; i < cid_list->count; i++) {
206 			if (!ispnpidacpi(cid_list->id[i].value))
207 				continue;
208 			dev_id = pnpacpi_kmalloc(sizeof(struct pnp_id),
209 				GFP_KERNEL);
210 			if (!dev_id)
211 				continue;
212 
213 			pnpidacpi_to_pnpid(cid_list->id[i].value, dev_id->id);
214 			pnp_add_id(dev_id, dev);
215 		}
216 	}
217 
218 	/* clear out the damaged flags */
219 	if (!dev->active)
220 		pnp_init_resource_table(&dev->res);
221 	pnp_add_device(dev);
222 	num ++;
223 
224 	return AE_OK;
225 err1:
226 	kfree(dev_id);
227 err:
228 	kfree(dev);
229 	return -EINVAL;
230 }
231 
232 static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
233 	u32 lvl, void *context, void **rv)
234 {
235 	struct acpi_device *device;
236 
237 	if (!acpi_bus_get_device(handle, &device))
238 		pnpacpi_add_device(device);
239 	else
240 		return AE_CTRL_DEPTH;
241 	return AE_OK;
242 }
243 
244 int pnpacpi_disabled __initdata;
245 int __init pnpacpi_init(void)
246 {
247 	if (acpi_disabled || pnpacpi_disabled) {
248 		pnp_info("PnP ACPI: disabled");
249 		return 0;
250 	}
251 	pnp_info("PnP ACPI init");
252 	pnp_register_protocol(&pnpacpi_protocol);
253 	acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL);
254 	pnp_info("PnP ACPI: found %d devices", num);
255 	return 0;
256 }
257 subsys_initcall(pnpacpi_init);
258 
259 static int __init pnpacpi_setup(char *str)
260 {
261 	if (str == NULL)
262 		return 1;
263 	if (!strncmp(str, "off", 3))
264 		pnpacpi_disabled = 1;
265 	return 1;
266 }
267 __setup("pnpacpi=", pnpacpi_setup);
268 
269 EXPORT_SYMBOL(pnpacpi_protocol);
270