Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * manager.c - Resource Management, Conflict Resolution, Activation and Disabling of Devices
4 : : *
5 : : * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz>
6 : : * Copyright 2003 Adam Belay <ambx1@neo.rr.com>
7 : : * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
8 : : * Bjorn Helgaas <bjorn.helgaas@hp.com>
9 : : */
10 : :
11 : : #include <linux/errno.h>
12 : : #include <linux/module.h>
13 : : #include <linux/init.h>
14 : : #include <linux/kernel.h>
15 : : #include <linux/pnp.h>
16 : : #include <linux/bitmap.h>
17 : : #include <linux/mutex.h>
18 : : #include "base.h"
19 : :
20 : : DEFINE_MUTEX(pnp_res_mutex);
21 : :
22 : 0 : static struct resource *pnp_find_resource(struct pnp_dev *dev,
23 : : unsigned char rule,
24 : : unsigned long type,
25 : : unsigned int bar)
26 : : {
27 : 0 : struct resource *res = pnp_get_resource(dev, type, bar);
28 : :
29 : : /* when the resource already exists, set its resource bits from rule */
30 [ # # # # : 0 : if (res) {
# # ]
31 : 0 : res->flags &= ~IORESOURCE_BITS;
32 : 0 : res->flags |= rule & IORESOURCE_BITS;
33 : : }
34 : :
35 : 0 : return res;
36 : : }
37 : :
38 : 0 : static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
39 : : {
40 : 0 : struct resource *res, local_res;
41 : :
42 : 0 : res = pnp_find_resource(dev, rule->flags, IORESOURCE_IO, idx);
43 [ # # ]: 0 : if (res) {
44 [ # # ]: 0 : pnp_dbg(&dev->dev, " io %d already set to %#llx-%#llx "
45 : : "flags %#lx\n", idx, (unsigned long long) res->start,
46 : : (unsigned long long) res->end, res->flags);
47 : 0 : return 0;
48 : : }
49 : :
50 : 0 : res = &local_res;
51 : 0 : res->flags = rule->flags | IORESOURCE_AUTO;
52 : 0 : res->start = 0;
53 : 0 : res->end = 0;
54 : :
55 [ # # ]: 0 : if (!rule->size) {
56 : 0 : res->flags |= IORESOURCE_DISABLED;
57 [ # # ]: 0 : pnp_dbg(&dev->dev, " io %d disabled\n", idx);
58 : 0 : goto __add;
59 : : }
60 : :
61 : 0 : res->start = rule->min;
62 : 0 : res->end = res->start + rule->size - 1;
63 : :
64 [ # # ]: 0 : while (!pnp_check_port(dev, res)) {
65 : 0 : res->start += rule->align;
66 : 0 : res->end = res->start + rule->size - 1;
67 [ # # # # ]: 0 : if (res->start > rule->max || !rule->align) {
68 [ # # ]: 0 : pnp_dbg(&dev->dev, " couldn't assign io %d "
69 : : "(min %#llx max %#llx)\n", idx,
70 : : (unsigned long long) rule->min,
71 : : (unsigned long long) rule->max);
72 : 0 : return -EBUSY;
73 : : }
74 : : }
75 : :
76 : 0 : __add:
77 : 0 : pnp_add_io_resource(dev, res->start, res->end, res->flags);
78 : 0 : return 0;
79 : : }
80 : :
81 : 0 : static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
82 : : {
83 : 0 : struct resource *res, local_res;
84 : :
85 : 0 : res = pnp_find_resource(dev, rule->flags, IORESOURCE_MEM, idx);
86 [ # # ]: 0 : if (res) {
87 [ # # ]: 0 : pnp_dbg(&dev->dev, " mem %d already set to %#llx-%#llx "
88 : : "flags %#lx\n", idx, (unsigned long long) res->start,
89 : : (unsigned long long) res->end, res->flags);
90 : 0 : return 0;
91 : : }
92 : :
93 : 0 : res = &local_res;
94 : 0 : res->flags = rule->flags | IORESOURCE_AUTO;
95 : 0 : res->start = 0;
96 : 0 : res->end = 0;
97 : :
98 : : /* ??? rule->flags restricted to 8 bits, all tests bogus ??? */
99 [ # # ]: 0 : if (!(rule->flags & IORESOURCE_MEM_WRITEABLE))
100 : 0 : res->flags |= IORESOURCE_READONLY;
101 [ # # ]: 0 : if (rule->flags & IORESOURCE_MEM_RANGELENGTH)
102 : 0 : res->flags |= IORESOURCE_RANGELENGTH;
103 [ # # ]: 0 : if (rule->flags & IORESOURCE_MEM_SHADOWABLE)
104 : 0 : res->flags |= IORESOURCE_SHADOWABLE;
105 : :
106 [ # # ]: 0 : if (!rule->size) {
107 : 0 : res->flags |= IORESOURCE_DISABLED;
108 [ # # ]: 0 : pnp_dbg(&dev->dev, " mem %d disabled\n", idx);
109 : 0 : goto __add;
110 : : }
111 : :
112 : 0 : res->start = rule->min;
113 : 0 : res->end = res->start + rule->size - 1;
114 : :
115 [ # # ]: 0 : while (!pnp_check_mem(dev, res)) {
116 : 0 : res->start += rule->align;
117 : 0 : res->end = res->start + rule->size - 1;
118 [ # # # # ]: 0 : if (res->start > rule->max || !rule->align) {
119 [ # # ]: 0 : pnp_dbg(&dev->dev, " couldn't assign mem %d "
120 : : "(min %#llx max %#llx)\n", idx,
121 : : (unsigned long long) rule->min,
122 : : (unsigned long long) rule->max);
123 : 0 : return -EBUSY;
124 : : }
125 : : }
126 : :
127 : 0 : __add:
128 : 0 : pnp_add_mem_resource(dev, res->start, res->end, res->flags);
129 : 0 : return 0;
130 : : }
131 : :
132 : 0 : static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
133 : : {
134 : 0 : struct resource *res, local_res;
135 : 0 : int i;
136 : :
137 : : /* IRQ priority: this table is good for i386 */
138 : 0 : static unsigned short xtab[16] = {
139 : : 5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2
140 : : };
141 : :
142 : 0 : res = pnp_find_resource(dev, rule->flags, IORESOURCE_IRQ, idx);
143 [ # # ]: 0 : if (res) {
144 [ # # ]: 0 : pnp_dbg(&dev->dev, " irq %d already set to %d flags %#lx\n",
145 : : idx, (int) res->start, res->flags);
146 : 0 : return 0;
147 : : }
148 : :
149 : 0 : res = &local_res;
150 : 0 : res->flags = rule->flags | IORESOURCE_AUTO;
151 : 0 : res->start = -1;
152 : 0 : res->end = -1;
153 : :
154 [ # # ]: 0 : if (bitmap_empty(rule->map.bits, PNP_IRQ_NR)) {
155 : 0 : res->flags |= IORESOURCE_DISABLED;
156 [ # # ]: 0 : pnp_dbg(&dev->dev, " irq %d disabled\n", idx);
157 : 0 : goto __add;
158 : : }
159 : :
160 : : /* TBD: need check for >16 IRQ */
161 : 0 : res->start = find_next_bit(rule->map.bits, PNP_IRQ_NR, 16);
162 [ # # ]: 0 : if (res->start < PNP_IRQ_NR) {
163 : 0 : res->end = res->start;
164 : 0 : goto __add;
165 : : }
166 [ # # ]: 0 : for (i = 0; i < 16; i++) {
167 [ # # ]: 0 : if (test_bit(xtab[i], rule->map.bits)) {
168 : 0 : res->start = res->end = xtab[i];
169 [ # # ]: 0 : if (pnp_check_irq(dev, res))
170 : 0 : goto __add;
171 : : }
172 : : }
173 : :
174 [ # # ]: 0 : if (rule->flags & IORESOURCE_IRQ_OPTIONAL) {
175 : 0 : res->start = -1;
176 : 0 : res->end = -1;
177 : 0 : res->flags |= IORESOURCE_DISABLED;
178 [ # # ]: 0 : pnp_dbg(&dev->dev, " irq %d disabled (optional)\n", idx);
179 : 0 : goto __add;
180 : : }
181 : :
182 [ # # ]: 0 : pnp_dbg(&dev->dev, " couldn't assign irq %d\n", idx);
183 : : return -EBUSY;
184 : :
185 : 0 : __add:
186 : 0 : pnp_add_irq_resource(dev, res->start, res->flags);
187 : 0 : return 0;
188 : : }
189 : :
190 : : #ifdef CONFIG_ISA_DMA_API
191 : : static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
192 : : {
193 : : struct resource *res, local_res;
194 : : int i;
195 : :
196 : : /* DMA priority: this table is good for i386 */
197 : : static unsigned short xtab[8] = {
198 : : 1, 3, 5, 6, 7, 0, 2, 4
199 : : };
200 : :
201 : : res = pnp_find_resource(dev, rule->flags, IORESOURCE_DMA, idx);
202 : : if (res) {
203 : : pnp_dbg(&dev->dev, " dma %d already set to %d flags %#lx\n",
204 : : idx, (int) res->start, res->flags);
205 : : return 0;
206 : : }
207 : :
208 : : res = &local_res;
209 : : res->flags = rule->flags | IORESOURCE_AUTO;
210 : : res->start = -1;
211 : : res->end = -1;
212 : :
213 : : if (!rule->map) {
214 : : res->flags |= IORESOURCE_DISABLED;
215 : : pnp_dbg(&dev->dev, " dma %d disabled\n", idx);
216 : : goto __add;
217 : : }
218 : :
219 : : for (i = 0; i < 8; i++) {
220 : : if (rule->map & (1 << xtab[i])) {
221 : : res->start = res->end = xtab[i];
222 : : if (pnp_check_dma(dev, res))
223 : : goto __add;
224 : : }
225 : : }
226 : :
227 : : pnp_dbg(&dev->dev, " couldn't assign dma %d\n", idx);
228 : : return -EBUSY;
229 : :
230 : : __add:
231 : : pnp_add_dma_resource(dev, res->start, res->flags);
232 : : return 0;
233 : : }
234 : : #endif /* CONFIG_ISA_DMA_API */
235 : :
236 : 168 : void pnp_init_resources(struct pnp_dev *dev)
237 : : {
238 : 168 : pnp_free_resources(dev);
239 : 168 : }
240 : :
241 : 0 : static void pnp_clean_resource_table(struct pnp_dev *dev)
242 : : {
243 : 0 : struct pnp_resource *pnp_res, *tmp;
244 : :
245 [ # # ]: 0 : list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) {
246 [ # # ]: 0 : if (pnp_res->res.flags & IORESOURCE_AUTO)
247 : 0 : pnp_free_resource(pnp_res);
248 : : }
249 : 0 : }
250 : :
251 : : /**
252 : : * pnp_assign_resources - assigns resources to the device based on the specified dependent number
253 : : * @dev: pointer to the desired device
254 : : * @set: the dependent function number
255 : : */
256 : 0 : static int pnp_assign_resources(struct pnp_dev *dev, int set)
257 : : {
258 : 0 : struct pnp_option *option;
259 : 0 : int nport = 0, nmem = 0, nirq = 0;
260 : 0 : int ndma __maybe_unused = 0;
261 : 0 : int ret = 0;
262 : :
263 [ # # ]: 0 : pnp_dbg(&dev->dev, "pnp_assign_resources, try dependent set %d\n", set);
264 : 0 : mutex_lock(&pnp_res_mutex);
265 : 0 : pnp_clean_resource_table(dev);
266 : :
267 [ # # ]: 0 : list_for_each_entry(option, &dev->options, list) {
268 [ # # ]: 0 : if (pnp_option_is_dependent(option) &&
269 [ # # ]: 0 : pnp_option_set(option) != set)
270 : 0 : continue;
271 : :
272 [ # # # # : 0 : switch (option->type) {
# ]
273 : 0 : case IORESOURCE_IO:
274 : 0 : ret = pnp_assign_port(dev, &option->u.port, nport++);
275 : 0 : break;
276 : 0 : case IORESOURCE_MEM:
277 : 0 : ret = pnp_assign_mem(dev, &option->u.mem, nmem++);
278 : 0 : break;
279 : 0 : case IORESOURCE_IRQ:
280 : 0 : ret = pnp_assign_irq(dev, &option->u.irq, nirq++);
281 : 0 : break;
282 : : #ifdef CONFIG_ISA_DMA_API
283 : 0 : case IORESOURCE_DMA:
284 : 0 : ret = pnp_assign_dma(dev, &option->u.dma, ndma++);
285 : 0 : break;
286 : : #endif
287 : : default:
288 : : ret = -EINVAL;
289 : : break;
290 : : }
291 [ # # ]: 0 : if (ret < 0)
292 : : break;
293 : : }
294 : :
295 : 0 : mutex_unlock(&pnp_res_mutex);
296 [ # # ]: 0 : if (ret < 0) {
297 [ # # ]: 0 : pnp_dbg(&dev->dev, "pnp_assign_resources failed (%d)\n", ret);
298 : 0 : pnp_clean_resource_table(dev);
299 : : } else
300 : 0 : dbg_pnp_show_resources(dev, "pnp_assign_resources succeeded");
301 : 0 : return ret;
302 : : }
303 : :
304 : : /**
305 : : * pnp_auto_config_dev - automatically assigns resources to a device
306 : : * @dev: pointer to the desired device
307 : : */
308 : 0 : int pnp_auto_config_dev(struct pnp_dev *dev)
309 : : {
310 : 0 : int i, ret;
311 : :
312 [ # # ]: 0 : if (!pnp_can_configure(dev)) {
313 [ # # ]: 0 : pnp_dbg(&dev->dev, "configuration not supported\n");
314 : 0 : return -ENODEV;
315 : : }
316 : :
317 : 0 : ret = pnp_assign_resources(dev, 0);
318 [ # # ]: 0 : if (ret == 0)
319 : : return 0;
320 : :
321 [ # # ]: 0 : for (i = 1; i < dev->num_dependent_sets; i++) {
322 : 0 : ret = pnp_assign_resources(dev, i);
323 [ # # ]: 0 : if (ret == 0)
324 : : return 0;
325 : : }
326 : :
327 : 0 : dev_err(&dev->dev, "unable to assign resources\n");
328 : 0 : return ret;
329 : : }
330 : :
331 : : /**
332 : : * pnp_start_dev - low-level start of the PnP device
333 : : * @dev: pointer to the desired device
334 : : *
335 : : * assumes that resources have already been allocated
336 : : */
337 : 0 : int pnp_start_dev(struct pnp_dev *dev)
338 : : {
339 [ # # # # ]: 0 : if (!pnp_can_write(dev)) {
340 [ # # ]: 0 : pnp_dbg(&dev->dev, "activation not supported\n");
341 : 0 : return -EINVAL;
342 : : }
343 : :
344 : 0 : dbg_pnp_show_resources(dev, "pnp_start_dev");
345 [ # # ]: 0 : if (dev->protocol->set(dev) < 0) {
346 : 0 : dev_err(&dev->dev, "activation failed\n");
347 : 0 : return -EIO;
348 : : }
349 : :
350 : 0 : dev_info(&dev->dev, "activated\n");
351 : 0 : return 0;
352 : : }
353 : :
354 : : /**
355 : : * pnp_stop_dev - low-level disable of the PnP device
356 : : * @dev: pointer to the desired device
357 : : *
358 : : * does not free resources
359 : : */
360 : 0 : int pnp_stop_dev(struct pnp_dev *dev)
361 : : {
362 [ # # # # : 0 : if (!pnp_can_disable(dev)) {
# # # # ]
363 [ # # ]: 0 : pnp_dbg(&dev->dev, "disabling not supported\n");
364 : 0 : return -EINVAL;
365 : : }
366 [ # # ]: 0 : if (dev->protocol->disable(dev) < 0) {
367 : 0 : dev_err(&dev->dev, "disable failed\n");
368 : 0 : return -EIO;
369 : : }
370 : :
371 : 0 : dev_info(&dev->dev, "disabled\n");
372 : 0 : return 0;
373 : : }
374 : :
375 : : /**
376 : : * pnp_activate_dev - activates a PnP device for use
377 : : * @dev: pointer to the desired device
378 : : *
379 : : * does not validate or set resources so be careful.
380 : : */
381 : 0 : int pnp_activate_dev(struct pnp_dev *dev)
382 : : {
383 : 0 : int error;
384 : :
385 [ # # ]: 0 : if (dev->active)
386 : : return 0;
387 : :
388 : : /* ensure resources are allocated */
389 [ # # ]: 0 : if (pnp_auto_config_dev(dev))
390 : : return -EBUSY;
391 : :
392 : 0 : error = pnp_start_dev(dev);
393 [ # # ]: 0 : if (error)
394 : : return error;
395 : :
396 : 0 : dev->active = 1;
397 : 0 : return 0;
398 : : }
399 : :
400 : : /**
401 : : * pnp_disable_dev - disables device
402 : : * @dev: pointer to the desired device
403 : : *
404 : : * inform the correct pnp protocol so that resources can be used by other devices
405 : : */
406 : 0 : int pnp_disable_dev(struct pnp_dev *dev)
407 : : {
408 : 0 : int error;
409 : :
410 [ # # ]: 0 : if (!dev->active)
411 : : return 0;
412 : :
413 : 0 : error = pnp_stop_dev(dev);
414 [ # # ]: 0 : if (error)
415 : : return error;
416 : :
417 : 0 : dev->active = 0;
418 : :
419 : : /* release the resources so that other devices can use them */
420 : 0 : mutex_lock(&pnp_res_mutex);
421 : 0 : pnp_clean_resource_table(dev);
422 : 0 : mutex_unlock(&pnp_res_mutex);
423 : :
424 : 0 : return 0;
425 : : }
426 : :
427 : : EXPORT_SYMBOL(pnp_start_dev);
428 : : EXPORT_SYMBOL(pnp_stop_dev);
429 : : EXPORT_SYMBOL(pnp_activate_dev);
430 : : EXPORT_SYMBOL(pnp_disable_dev);
|