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