]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * resource.c - Contains functions for registering and analyzing resource information | |
3 | * | |
4 | * based on isapnp.c resource management (c) Jaroslav Kysela <[email protected]> | |
5 | * Copyright 2003 Adam Belay <[email protected]> | |
6 | * | |
7 | */ | |
8 | ||
1da177e4 LT |
9 | #include <linux/module.h> |
10 | #include <linux/errno.h> | |
11 | #include <linux/interrupt.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <asm/io.h> | |
14 | #include <asm/dma.h> | |
15 | #include <asm/irq.h> | |
16 | #include <linux/pci.h> | |
17 | #include <linux/ioport.h> | |
18 | #include <linux/init.h> | |
19 | ||
20 | #include <linux/pnp.h> | |
21 | #include "base.h" | |
22 | ||
23 | static int pnp_reserve_irq[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some IRQ */ | |
24 | static int pnp_reserve_dma[8] = { [0 ... 7] = -1 }; /* reserve (don't use) some DMA */ | |
25 | static int pnp_reserve_io[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some I/O region */ | |
26 | static int pnp_reserve_mem[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some memory region */ | |
27 | ||
28 | ||
29 | /* | |
30 | * option registration | |
31 | */ | |
32 | ||
33 | static struct pnp_option * pnp_build_option(int priority) | |
34 | { | |
35 | struct pnp_option *option = pnp_alloc(sizeof(struct pnp_option)); | |
36 | ||
37 | /* check if pnp_alloc ran out of memory */ | |
38 | if (!option) | |
39 | return NULL; | |
40 | ||
41 | option->priority = priority & 0xff; | |
42 | /* make sure the priority is valid */ | |
43 | if (option->priority > PNP_RES_PRIORITY_FUNCTIONAL) | |
44 | option->priority = PNP_RES_PRIORITY_INVALID; | |
45 | ||
46 | return option; | |
47 | } | |
48 | ||
49 | struct pnp_option * pnp_register_independent_option(struct pnp_dev *dev) | |
50 | { | |
51 | struct pnp_option *option; | |
52 | if (!dev) | |
53 | return NULL; | |
54 | ||
55 | option = pnp_build_option(PNP_RES_PRIORITY_PREFERRED); | |
56 | ||
57 | /* this should never happen but if it does we'll try to continue */ | |
58 | if (dev->independent) | |
59 | pnp_err("independent resource already registered"); | |
60 | dev->independent = option; | |
61 | return option; | |
62 | } | |
63 | ||
64 | struct pnp_option * pnp_register_dependent_option(struct pnp_dev *dev, int priority) | |
65 | { | |
66 | struct pnp_option *option; | |
67 | if (!dev) | |
68 | return NULL; | |
69 | ||
70 | option = pnp_build_option(priority); | |
71 | ||
72 | if (dev->dependent) { | |
73 | struct pnp_option *parent = dev->dependent; | |
74 | while (parent->next) | |
75 | parent = parent->next; | |
76 | parent->next = option; | |
77 | } else | |
78 | dev->dependent = option; | |
79 | return option; | |
80 | } | |
81 | ||
82 | int pnp_register_irq_resource(struct pnp_option *option, struct pnp_irq *data) | |
83 | { | |
84 | struct pnp_irq *ptr; | |
85 | if (!option) | |
86 | return -EINVAL; | |
87 | if (!data) | |
88 | return -EINVAL; | |
89 | ||
90 | ptr = option->irq; | |
91 | while (ptr && ptr->next) | |
92 | ptr = ptr->next; | |
93 | if (ptr) | |
94 | ptr->next = data; | |
95 | else | |
96 | option->irq = data; | |
97 | ||
98 | #ifdef CONFIG_PCI | |
99 | { | |
100 | int i; | |
101 | ||
102 | for (i = 0; i < 16; i++) | |
103 | if (test_bit(i, data->map)) | |
c9c3e457 | 104 | pcibios_penalize_isa_irq(i, 0); |
1da177e4 LT |
105 | } |
106 | #endif | |
107 | return 0; | |
108 | } | |
109 | ||
110 | int pnp_register_dma_resource(struct pnp_option *option, struct pnp_dma *data) | |
111 | { | |
112 | struct pnp_dma *ptr; | |
113 | if (!option) | |
114 | return -EINVAL; | |
115 | if (!data) | |
116 | return -EINVAL; | |
117 | ||
118 | ptr = option->dma; | |
119 | while (ptr && ptr->next) | |
120 | ptr = ptr->next; | |
121 | if (ptr) | |
122 | ptr->next = data; | |
123 | else | |
124 | option->dma = data; | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | int pnp_register_port_resource(struct pnp_option *option, struct pnp_port *data) | |
130 | { | |
131 | struct pnp_port *ptr; | |
132 | if (!option) | |
133 | return -EINVAL; | |
134 | if (!data) | |
135 | return -EINVAL; | |
136 | ||
137 | ptr = option->port; | |
138 | while (ptr && ptr->next) | |
139 | ptr = ptr->next; | |
140 | if (ptr) | |
141 | ptr->next = data; | |
142 | else | |
143 | option->port = data; | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | int pnp_register_mem_resource(struct pnp_option *option, struct pnp_mem *data) | |
149 | { | |
150 | struct pnp_mem *ptr; | |
151 | if (!option) | |
152 | return -EINVAL; | |
153 | if (!data) | |
154 | return -EINVAL; | |
155 | ||
156 | ptr = option->mem; | |
157 | while (ptr && ptr->next) | |
158 | ptr = ptr->next; | |
159 | if (ptr) | |
160 | ptr->next = data; | |
161 | else | |
162 | option->mem = data; | |
163 | return 0; | |
164 | } | |
165 | ||
166 | static void pnp_free_port(struct pnp_port *port) | |
167 | { | |
168 | struct pnp_port *next; | |
169 | ||
170 | while (port) { | |
171 | next = port->next; | |
172 | kfree(port); | |
173 | port = next; | |
174 | } | |
175 | } | |
176 | ||
177 | static void pnp_free_irq(struct pnp_irq *irq) | |
178 | { | |
179 | struct pnp_irq *next; | |
180 | ||
181 | while (irq) { | |
182 | next = irq->next; | |
183 | kfree(irq); | |
184 | irq = next; | |
185 | } | |
186 | } | |
187 | ||
188 | static void pnp_free_dma(struct pnp_dma *dma) | |
189 | { | |
190 | struct pnp_dma *next; | |
191 | ||
192 | while (dma) { | |
193 | next = dma->next; | |
194 | kfree(dma); | |
195 | dma = next; | |
196 | } | |
197 | } | |
198 | ||
199 | static void pnp_free_mem(struct pnp_mem *mem) | |
200 | { | |
201 | struct pnp_mem *next; | |
202 | ||
203 | while (mem) { | |
204 | next = mem->next; | |
205 | kfree(mem); | |
206 | mem = next; | |
207 | } | |
208 | } | |
209 | ||
210 | void pnp_free_option(struct pnp_option *option) | |
211 | { | |
212 | struct pnp_option *next; | |
213 | ||
214 | while (option) { | |
215 | next = option->next; | |
216 | pnp_free_port(option->port); | |
217 | pnp_free_irq(option->irq); | |
218 | pnp_free_dma(option->dma); | |
219 | pnp_free_mem(option->mem); | |
220 | kfree(option); | |
221 | option = next; | |
222 | } | |
223 | } | |
224 | ||
225 | ||
226 | /* | |
227 | * resource validity checking | |
228 | */ | |
229 | ||
230 | #define length(start, end) (*(end) - *(start) + 1) | |
231 | ||
232 | /* Two ranges conflict if one doesn't end before the other starts */ | |
233 | #define ranged_conflict(starta, enda, startb, endb) \ | |
234 | !((*(enda) < *(startb)) || (*(endb) < *(starta))) | |
235 | ||
236 | #define cannot_compare(flags) \ | |
237 | ((flags) & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) | |
238 | ||
239 | int pnp_check_port(struct pnp_dev * dev, int idx) | |
240 | { | |
241 | int tmp; | |
242 | struct pnp_dev *tdev; | |
b60ba834 | 243 | resource_size_t *port, *end, *tport, *tend; |
1da177e4 LT |
244 | port = &dev->res.port_resource[idx].start; |
245 | end = &dev->res.port_resource[idx].end; | |
246 | ||
247 | /* if the resource doesn't exist, don't complain about it */ | |
248 | if (cannot_compare(dev->res.port_resource[idx].flags)) | |
249 | return 1; | |
250 | ||
251 | /* check if the resource is already in use, skip if the | |
252 | * device is active because it itself may be in use */ | |
253 | if(!dev->active) { | |
254 | if (__check_region(&ioport_resource, *port, length(port,end))) | |
255 | return 0; | |
256 | } | |
257 | ||
258 | /* check if the resource is reserved */ | |
259 | for (tmp = 0; tmp < 8; tmp++) { | |
260 | int rport = pnp_reserve_io[tmp << 1]; | |
261 | int rend = pnp_reserve_io[(tmp << 1) + 1] + rport - 1; | |
262 | if (ranged_conflict(port,end,&rport,&rend)) | |
263 | return 0; | |
264 | } | |
265 | ||
266 | /* check for internal conflicts */ | |
267 | for (tmp = 0; tmp < PNP_MAX_PORT && tmp != idx; tmp++) { | |
268 | if (dev->res.port_resource[tmp].flags & IORESOURCE_IO) { | |
269 | tport = &dev->res.port_resource[tmp].start; | |
270 | tend = &dev->res.port_resource[tmp].end; | |
271 | if (ranged_conflict(port,end,tport,tend)) | |
272 | return 0; | |
273 | } | |
274 | } | |
275 | ||
276 | /* check for conflicts with other pnp devices */ | |
277 | pnp_for_each_dev(tdev) { | |
278 | if (tdev == dev) | |
279 | continue; | |
280 | for (tmp = 0; tmp < PNP_MAX_PORT; tmp++) { | |
281 | if (tdev->res.port_resource[tmp].flags & IORESOURCE_IO) { | |
282 | if (cannot_compare(tdev->res.port_resource[tmp].flags)) | |
283 | continue; | |
284 | tport = &tdev->res.port_resource[tmp].start; | |
285 | tend = &tdev->res.port_resource[tmp].end; | |
286 | if (ranged_conflict(port,end,tport,tend)) | |
287 | return 0; | |
288 | } | |
289 | } | |
290 | } | |
291 | ||
292 | return 1; | |
293 | } | |
294 | ||
295 | int pnp_check_mem(struct pnp_dev * dev, int idx) | |
296 | { | |
297 | int tmp; | |
298 | struct pnp_dev *tdev; | |
b60ba834 | 299 | resource_size_t *addr, *end, *taddr, *tend; |
1da177e4 LT |
300 | addr = &dev->res.mem_resource[idx].start; |
301 | end = &dev->res.mem_resource[idx].end; | |
302 | ||
303 | /* if the resource doesn't exist, don't complain about it */ | |
304 | if (cannot_compare(dev->res.mem_resource[idx].flags)) | |
305 | return 1; | |
306 | ||
307 | /* check if the resource is already in use, skip if the | |
308 | * device is active because it itself may be in use */ | |
309 | if(!dev->active) { | |
310 | if (check_mem_region(*addr, length(addr,end))) | |
311 | return 0; | |
312 | } | |
313 | ||
314 | /* check if the resource is reserved */ | |
315 | for (tmp = 0; tmp < 8; tmp++) { | |
316 | int raddr = pnp_reserve_mem[tmp << 1]; | |
317 | int rend = pnp_reserve_mem[(tmp << 1) + 1] + raddr - 1; | |
318 | if (ranged_conflict(addr,end,&raddr,&rend)) | |
319 | return 0; | |
320 | } | |
321 | ||
322 | /* check for internal conflicts */ | |
323 | for (tmp = 0; tmp < PNP_MAX_MEM && tmp != idx; tmp++) { | |
324 | if (dev->res.mem_resource[tmp].flags & IORESOURCE_MEM) { | |
325 | taddr = &dev->res.mem_resource[tmp].start; | |
326 | tend = &dev->res.mem_resource[tmp].end; | |
327 | if (ranged_conflict(addr,end,taddr,tend)) | |
328 | return 0; | |
329 | } | |
330 | } | |
331 | ||
332 | /* check for conflicts with other pnp devices */ | |
333 | pnp_for_each_dev(tdev) { | |
334 | if (tdev == dev) | |
335 | continue; | |
336 | for (tmp = 0; tmp < PNP_MAX_MEM; tmp++) { | |
337 | if (tdev->res.mem_resource[tmp].flags & IORESOURCE_MEM) { | |
338 | if (cannot_compare(tdev->res.mem_resource[tmp].flags)) | |
339 | continue; | |
340 | taddr = &tdev->res.mem_resource[tmp].start; | |
341 | tend = &tdev->res.mem_resource[tmp].end; | |
342 | if (ranged_conflict(addr,end,taddr,tend)) | |
343 | return 0; | |
344 | } | |
345 | } | |
346 | } | |
347 | ||
348 | return 1; | |
349 | } | |
350 | ||
351 | static irqreturn_t pnp_test_handler(int irq, void *dev_id, struct pt_regs *regs) | |
352 | { | |
353 | return IRQ_HANDLED; | |
354 | } | |
355 | ||
356 | int pnp_check_irq(struct pnp_dev * dev, int idx) | |
357 | { | |
358 | int tmp; | |
359 | struct pnp_dev *tdev; | |
b60ba834 | 360 | resource_size_t * irq = &dev->res.irq_resource[idx].start; |
1da177e4 LT |
361 | |
362 | /* if the resource doesn't exist, don't complain about it */ | |
363 | if (cannot_compare(dev->res.irq_resource[idx].flags)) | |
364 | return 1; | |
365 | ||
366 | /* check if the resource is valid */ | |
367 | if (*irq < 0 || *irq > 15) | |
368 | return 0; | |
369 | ||
370 | /* check if the resource is reserved */ | |
371 | for (tmp = 0; tmp < 16; tmp++) { | |
372 | if (pnp_reserve_irq[tmp] == *irq) | |
373 | return 0; | |
374 | } | |
375 | ||
376 | /* check for internal conflicts */ | |
377 | for (tmp = 0; tmp < PNP_MAX_IRQ && tmp != idx; tmp++) { | |
378 | if (dev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) { | |
379 | if (dev->res.irq_resource[tmp].start == *irq) | |
380 | return 0; | |
381 | } | |
382 | } | |
383 | ||
384 | #ifdef CONFIG_PCI | |
385 | /* check if the resource is being used by a pci device */ | |
386 | { | |
387 | struct pci_dev *pci = NULL; | |
388 | for_each_pci_dev(pci) { | |
389 | if (pci->irq == *irq) | |
390 | return 0; | |
391 | } | |
392 | } | |
393 | #endif | |
394 | ||
395 | /* check if the resource is already in use, skip if the | |
396 | * device is active because it itself may be in use */ | |
397 | if(!dev->active) { | |
0cadaf45 | 398 | if (request_irq(*irq, pnp_test_handler, |
dace1453 | 399 | IRQF_DISABLED|IRQF_PROBE_SHARED, "pnp", NULL)) |
1da177e4 LT |
400 | return 0; |
401 | free_irq(*irq, NULL); | |
402 | } | |
403 | ||
404 | /* check for conflicts with other pnp devices */ | |
405 | pnp_for_each_dev(tdev) { | |
406 | if (tdev == dev) | |
407 | continue; | |
408 | for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) { | |
409 | if (tdev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) { | |
410 | if (cannot_compare(tdev->res.irq_resource[tmp].flags)) | |
411 | continue; | |
412 | if ((tdev->res.irq_resource[tmp].start == *irq)) | |
413 | return 0; | |
414 | } | |
415 | } | |
416 | } | |
417 | ||
418 | return 1; | |
419 | } | |
420 | ||
421 | int pnp_check_dma(struct pnp_dev * dev, int idx) | |
422 | { | |
423 | #ifndef CONFIG_IA64 | |
424 | int tmp; | |
425 | struct pnp_dev *tdev; | |
b60ba834 | 426 | resource_size_t * dma = &dev->res.dma_resource[idx].start; |
1da177e4 LT |
427 | |
428 | /* if the resource doesn't exist, don't complain about it */ | |
429 | if (cannot_compare(dev->res.dma_resource[idx].flags)) | |
430 | return 1; | |
431 | ||
432 | /* check if the resource is valid */ | |
433 | if (*dma < 0 || *dma == 4 || *dma > 7) | |
434 | return 0; | |
435 | ||
436 | /* check if the resource is reserved */ | |
437 | for (tmp = 0; tmp < 8; tmp++) { | |
438 | if (pnp_reserve_dma[tmp] == *dma) | |
439 | return 0; | |
440 | } | |
441 | ||
442 | /* check for internal conflicts */ | |
443 | for (tmp = 0; tmp < PNP_MAX_DMA && tmp != idx; tmp++) { | |
444 | if (dev->res.dma_resource[tmp].flags & IORESOURCE_DMA) { | |
445 | if (dev->res.dma_resource[tmp].start == *dma) | |
446 | return 0; | |
447 | } | |
448 | } | |
449 | ||
450 | /* check if the resource is already in use, skip if the | |
451 | * device is active because it itself may be in use */ | |
452 | if(!dev->active) { | |
453 | if (request_dma(*dma, "pnp")) | |
454 | return 0; | |
455 | free_dma(*dma); | |
456 | } | |
457 | ||
458 | /* check for conflicts with other pnp devices */ | |
459 | pnp_for_each_dev(tdev) { | |
460 | if (tdev == dev) | |
461 | continue; | |
462 | for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) { | |
463 | if (tdev->res.dma_resource[tmp].flags & IORESOURCE_DMA) { | |
464 | if (cannot_compare(tdev->res.dma_resource[tmp].flags)) | |
465 | continue; | |
466 | if ((tdev->res.dma_resource[tmp].start == *dma)) | |
467 | return 0; | |
468 | } | |
469 | } | |
470 | } | |
471 | ||
472 | return 1; | |
473 | #else | |
474 | /* IA64 hasn't legacy DMA */ | |
475 | return 0; | |
476 | #endif | |
477 | } | |
478 | ||
479 | ||
b449f63c | 480 | #if 0 |
1da177e4 LT |
481 | EXPORT_SYMBOL(pnp_register_dependent_option); |
482 | EXPORT_SYMBOL(pnp_register_independent_option); | |
483 | EXPORT_SYMBOL(pnp_register_irq_resource); | |
484 | EXPORT_SYMBOL(pnp_register_dma_resource); | |
485 | EXPORT_SYMBOL(pnp_register_port_resource); | |
486 | EXPORT_SYMBOL(pnp_register_mem_resource); | |
b449f63c | 487 | #endif /* 0 */ |
1da177e4 LT |
488 | |
489 | ||
490 | /* format is: pnp_reserve_irq=irq1[,irq2] .... */ | |
491 | ||
492 | static int __init pnp_setup_reserve_irq(char *str) | |
493 | { | |
494 | int i; | |
495 | ||
496 | for (i = 0; i < 16; i++) | |
497 | if (get_option(&str,&pnp_reserve_irq[i]) != 2) | |
498 | break; | |
499 | return 1; | |
500 | } | |
501 | ||
502 | __setup("pnp_reserve_irq=", pnp_setup_reserve_irq); | |
503 | ||
504 | /* format is: pnp_reserve_dma=dma1[,dma2] .... */ | |
505 | ||
506 | static int __init pnp_setup_reserve_dma(char *str) | |
507 | { | |
508 | int i; | |
509 | ||
510 | for (i = 0; i < 8; i++) | |
511 | if (get_option(&str,&pnp_reserve_dma[i]) != 2) | |
512 | break; | |
513 | return 1; | |
514 | } | |
515 | ||
516 | __setup("pnp_reserve_dma=", pnp_setup_reserve_dma); | |
517 | ||
518 | /* format is: pnp_reserve_io=io1,size1[,io2,size2] .... */ | |
519 | ||
520 | static int __init pnp_setup_reserve_io(char *str) | |
521 | { | |
522 | int i; | |
523 | ||
524 | for (i = 0; i < 16; i++) | |
525 | if (get_option(&str,&pnp_reserve_io[i]) != 2) | |
526 | break; | |
527 | return 1; | |
528 | } | |
529 | ||
530 | __setup("pnp_reserve_io=", pnp_setup_reserve_io); | |
531 | ||
532 | /* format is: pnp_reserve_mem=mem1,size1[,mem2,size2] .... */ | |
533 | ||
534 | static int __init pnp_setup_reserve_mem(char *str) | |
535 | { | |
536 | int i; | |
537 | ||
538 | for (i = 0; i < 16; i++) | |
539 | if (get_option(&str,&pnp_reserve_mem[i]) != 2) | |
540 | break; | |
541 | return 1; | |
542 | } | |
543 | ||
544 | __setup("pnp_reserve_mem=", pnp_setup_reserve_mem); |