Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * resource.c - Contains functions for registering and analyzing resource information | |
3 | * | |
c1017a4c | 4 | * based on isapnp.c resource management (c) Jaroslav Kysela <perex@perex.cz> |
1da177e4 | 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) | |
a05d0781 | 54 | dev_err(&dev->dev, "independent resource already registered\n"); |
1da177e4 LT |
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) { | |
8ea50a3f JL |
370 | if (pci->irq == *irq) { |
371 | pci_dev_put(pci); | |
1da177e4 | 372 | return 0; |
8ea50a3f | 373 | } |
1da177e4 LT |
374 | } |
375 | } | |
376 | #endif | |
377 | ||
378 | /* check if the resource is already in use, skip if the | |
379 | * device is active because it itself may be in use */ | |
9dd78466 | 380 | if (!dev->active) { |
0cadaf45 | 381 | if (request_irq(*irq, pnp_test_handler, |
9dd78466 | 382 | IRQF_DISABLED | IRQF_PROBE_SHARED, "pnp", NULL)) |
1da177e4 LT |
383 | return 0; |
384 | free_irq(*irq, NULL); | |
385 | } | |
386 | ||
387 | /* check for conflicts with other pnp devices */ | |
388 | pnp_for_each_dev(tdev) { | |
389 | if (tdev == dev) | |
390 | continue; | |
391 | for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) { | |
392 | if (tdev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) { | |
9dd78466 BH |
393 | if (cannot_compare |
394 | (tdev->res.irq_resource[tmp].flags)) | |
1da177e4 LT |
395 | continue; |
396 | if ((tdev->res.irq_resource[tmp].start == *irq)) | |
397 | return 0; | |
398 | } | |
399 | } | |
400 | } | |
401 | ||
402 | return 1; | |
403 | } | |
404 | ||
9dd78466 | 405 | int pnp_check_dma(struct pnp_dev *dev, int idx) |
1da177e4 LT |
406 | { |
407 | #ifndef CONFIG_IA64 | |
408 | int tmp; | |
409 | struct pnp_dev *tdev; | |
9dd78466 | 410 | resource_size_t *dma = &dev->res.dma_resource[idx].start; |
1da177e4 LT |
411 | |
412 | /* if the resource doesn't exist, don't complain about it */ | |
413 | if (cannot_compare(dev->res.dma_resource[idx].flags)) | |
414 | return 1; | |
415 | ||
416 | /* check if the resource is valid */ | |
417 | if (*dma < 0 || *dma == 4 || *dma > 7) | |
418 | return 0; | |
419 | ||
420 | /* check if the resource is reserved */ | |
421 | for (tmp = 0; tmp < 8; tmp++) { | |
422 | if (pnp_reserve_dma[tmp] == *dma) | |
423 | return 0; | |
424 | } | |
425 | ||
426 | /* check for internal conflicts */ | |
427 | for (tmp = 0; tmp < PNP_MAX_DMA && tmp != idx; tmp++) { | |
428 | if (dev->res.dma_resource[tmp].flags & IORESOURCE_DMA) { | |
429 | if (dev->res.dma_resource[tmp].start == *dma) | |
430 | return 0; | |
431 | } | |
432 | } | |
433 | ||
434 | /* check if the resource is already in use, skip if the | |
435 | * device is active because it itself may be in use */ | |
9dd78466 | 436 | if (!dev->active) { |
1da177e4 LT |
437 | if (request_dma(*dma, "pnp")) |
438 | return 0; | |
439 | free_dma(*dma); | |
440 | } | |
441 | ||
442 | /* check for conflicts with other pnp devices */ | |
443 | pnp_for_each_dev(tdev) { | |
444 | if (tdev == dev) | |
445 | continue; | |
446 | for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) { | |
447 | if (tdev->res.dma_resource[tmp].flags & IORESOURCE_DMA) { | |
9dd78466 BH |
448 | if (cannot_compare |
449 | (tdev->res.dma_resource[tmp].flags)) | |
1da177e4 LT |
450 | continue; |
451 | if ((tdev->res.dma_resource[tmp].start == *dma)) | |
452 | return 0; | |
453 | } | |
454 | } | |
455 | } | |
456 | ||
457 | return 1; | |
458 | #else | |
07d4e9af | 459 | /* IA64 does not have legacy DMA */ |
1da177e4 LT |
460 | return 0; |
461 | #endif | |
462 | } | |
463 | ||
1da177e4 | 464 | /* format is: pnp_reserve_irq=irq1[,irq2] .... */ |
1da177e4 LT |
465 | static int __init pnp_setup_reserve_irq(char *str) |
466 | { | |
467 | int i; | |
468 | ||
469 | for (i = 0; i < 16; i++) | |
9dd78466 | 470 | if (get_option(&str, &pnp_reserve_irq[i]) != 2) |
1da177e4 LT |
471 | break; |
472 | return 1; | |
473 | } | |
474 | ||
475 | __setup("pnp_reserve_irq=", pnp_setup_reserve_irq); | |
476 | ||
477 | /* format is: pnp_reserve_dma=dma1[,dma2] .... */ | |
1da177e4 LT |
478 | static int __init pnp_setup_reserve_dma(char *str) |
479 | { | |
480 | int i; | |
481 | ||
482 | for (i = 0; i < 8; i++) | |
9dd78466 | 483 | if (get_option(&str, &pnp_reserve_dma[i]) != 2) |
1da177e4 LT |
484 | break; |
485 | return 1; | |
486 | } | |
487 | ||
488 | __setup("pnp_reserve_dma=", pnp_setup_reserve_dma); | |
489 | ||
490 | /* format is: pnp_reserve_io=io1,size1[,io2,size2] .... */ | |
1da177e4 LT |
491 | static int __init pnp_setup_reserve_io(char *str) |
492 | { | |
493 | int i; | |
494 | ||
495 | for (i = 0; i < 16; i++) | |
9dd78466 | 496 | if (get_option(&str, &pnp_reserve_io[i]) != 2) |
1da177e4 LT |
497 | break; |
498 | return 1; | |
499 | } | |
500 | ||
501 | __setup("pnp_reserve_io=", pnp_setup_reserve_io); | |
502 | ||
503 | /* format is: pnp_reserve_mem=mem1,size1[,mem2,size2] .... */ | |
1da177e4 LT |
504 | static int __init pnp_setup_reserve_mem(char *str) |
505 | { | |
506 | int i; | |
507 | ||
508 | for (i = 0; i < 16; i++) | |
9dd78466 | 509 | if (get_option(&str, &pnp_reserve_mem[i]) != 2) |
1da177e4 LT |
510 | break; |
511 | return 1; | |
512 | } | |
513 | ||
514 | __setup("pnp_reserve_mem=", pnp_setup_reserve_mem); |