]>
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> |
1f32ca31 BH |
6 | * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. |
7 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | |
1da177e4 LT |
8 | */ |
9 | ||
1da177e4 | 10 | #include <linux/module.h> |
5a0e3ad6 | 11 | #include <linux/slab.h> |
1da177e4 LT |
12 | #include <linux/errno.h> |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <asm/io.h> | |
16 | #include <asm/dma.h> | |
17 | #include <asm/irq.h> | |
18 | #include <linux/pci.h> | |
19 | #include <linux/ioport.h> | |
20 | #include <linux/init.h> | |
21 | ||
22 | #include <linux/pnp.h> | |
23 | #include "base.h" | |
24 | ||
07d4e9af BH |
25 | static int pnp_reserve_irq[16] = {[0 ... 15] = -1 }; /* reserve (don't use) some IRQ */ |
26 | static int pnp_reserve_dma[8] = {[0 ... 7] = -1 }; /* reserve (don't use) some DMA */ | |
27 | static int pnp_reserve_io[16] = {[0 ... 15] = -1 }; /* reserve (don't use) some I/O region */ | |
28 | static int pnp_reserve_mem[16] = {[0 ... 15] = -1 }; /* reserve (don't use) some memory region */ | |
1da177e4 LT |
29 | |
30 | /* | |
31 | * option registration | |
32 | */ | |
33 | ||
1f32ca31 BH |
34 | struct pnp_option *pnp_build_option(struct pnp_dev *dev, unsigned long type, |
35 | unsigned int option_flags) | |
1da177e4 | 36 | { |
1f32ca31 | 37 | struct pnp_option *option; |
1da177e4 | 38 | |
1f32ca31 | 39 | option = kzalloc(sizeof(struct pnp_option), GFP_KERNEL); |
1da177e4 LT |
40 | if (!option) |
41 | return NULL; | |
42 | ||
1f32ca31 BH |
43 | option->flags = option_flags; |
44 | option->type = type; | |
07d4e9af | 45 | |
1f32ca31 | 46 | list_add_tail(&option->list, &dev->options); |
1da177e4 LT |
47 | return option; |
48 | } | |
49 | ||
1f32ca31 | 50 | int pnp_register_irq_resource(struct pnp_dev *dev, unsigned int option_flags, |
c227536b | 51 | pnp_irq_mask_t *map, unsigned char flags) |
1da177e4 | 52 | { |
1f32ca31 BH |
53 | struct pnp_option *option; |
54 | struct pnp_irq *irq; | |
07d4e9af | 55 | |
1f32ca31 BH |
56 | option = pnp_build_option(dev, IORESOURCE_IRQ, option_flags); |
57 | if (!option) | |
c227536b BH |
58 | return -ENOMEM; |
59 | ||
1f32ca31 | 60 | irq = &option->u.irq; |
2d29a7a7 BH |
61 | irq->map = *map; |
62 | irq->flags = flags; | |
c227536b | 63 | |
1da177e4 LT |
64 | #ifdef CONFIG_PCI |
65 | { | |
66 | int i; | |
67 | ||
68 | for (i = 0; i < 16; i++) | |
2d29a7a7 | 69 | if (test_bit(i, irq->map.bits)) |
c9c3e457 | 70 | pcibios_penalize_isa_irq(i, 0); |
1da177e4 LT |
71 | } |
72 | #endif | |
c1caf06c | 73 | |
1f32ca31 | 74 | dbg_pnp_show_option(dev, option); |
1da177e4 LT |
75 | return 0; |
76 | } | |
77 | ||
1f32ca31 | 78 | int pnp_register_dma_resource(struct pnp_dev *dev, unsigned int option_flags, |
c227536b | 79 | unsigned char map, unsigned char flags) |
1da177e4 | 80 | { |
1f32ca31 BH |
81 | struct pnp_option *option; |
82 | struct pnp_dma *dma; | |
c227536b | 83 | |
1f32ca31 BH |
84 | option = pnp_build_option(dev, IORESOURCE_DMA, option_flags); |
85 | if (!option) | |
c227536b BH |
86 | return -ENOMEM; |
87 | ||
1f32ca31 | 88 | dma = &option->u.dma; |
2d29a7a7 BH |
89 | dma->map = map; |
90 | dma->flags = flags; | |
07d4e9af | 91 | |
1f32ca31 | 92 | dbg_pnp_show_option(dev, option); |
1da177e4 LT |
93 | return 0; |
94 | } | |
95 | ||
1f32ca31 | 96 | int pnp_register_port_resource(struct pnp_dev *dev, unsigned int option_flags, |
c227536b BH |
97 | resource_size_t min, resource_size_t max, |
98 | resource_size_t align, resource_size_t size, | |
99 | unsigned char flags) | |
1da177e4 | 100 | { |
1f32ca31 BH |
101 | struct pnp_option *option; |
102 | struct pnp_port *port; | |
c227536b | 103 | |
1f32ca31 BH |
104 | option = pnp_build_option(dev, IORESOURCE_IO, option_flags); |
105 | if (!option) | |
c227536b BH |
106 | return -ENOMEM; |
107 | ||
1f32ca31 | 108 | port = &option->u.port; |
2d29a7a7 BH |
109 | port->min = min; |
110 | port->max = max; | |
111 | port->align = align; | |
112 | port->size = size; | |
113 | port->flags = flags; | |
07d4e9af | 114 | |
1f32ca31 | 115 | dbg_pnp_show_option(dev, option); |
1da177e4 LT |
116 | return 0; |
117 | } | |
118 | ||
1f32ca31 | 119 | int pnp_register_mem_resource(struct pnp_dev *dev, unsigned int option_flags, |
c227536b BH |
120 | resource_size_t min, resource_size_t max, |
121 | resource_size_t align, resource_size_t size, | |
122 | unsigned char flags) | |
1da177e4 | 123 | { |
1f32ca31 BH |
124 | struct pnp_option *option; |
125 | struct pnp_mem *mem; | |
c227536b | 126 | |
1f32ca31 BH |
127 | option = pnp_build_option(dev, IORESOURCE_MEM, option_flags); |
128 | if (!option) | |
c227536b BH |
129 | return -ENOMEM; |
130 | ||
1f32ca31 | 131 | mem = &option->u.mem; |
2d29a7a7 BH |
132 | mem->min = min; |
133 | mem->max = max; | |
134 | mem->align = align; | |
135 | mem->size = size; | |
136 | mem->flags = flags; | |
07d4e9af | 137 | |
1f32ca31 | 138 | dbg_pnp_show_option(dev, option); |
1da177e4 LT |
139 | return 0; |
140 | } | |
141 | ||
1f32ca31 | 142 | void pnp_free_options(struct pnp_dev *dev) |
1da177e4 | 143 | { |
1f32ca31 | 144 | struct pnp_option *option, *tmp; |
1da177e4 | 145 | |
1f32ca31 BH |
146 | list_for_each_entry_safe(option, tmp, &dev->options, list) { |
147 | list_del(&option->list); | |
1da177e4 | 148 | kfree(option); |
1da177e4 LT |
149 | } |
150 | } | |
151 | ||
1da177e4 LT |
152 | /* |
153 | * resource validity checking | |
154 | */ | |
155 | ||
156 | #define length(start, end) (*(end) - *(start) + 1) | |
157 | ||
158 | /* Two ranges conflict if one doesn't end before the other starts */ | |
159 | #define ranged_conflict(starta, enda, startb, endb) \ | |
160 | !((*(enda) < *(startb)) || (*(endb) < *(starta))) | |
161 | ||
162 | #define cannot_compare(flags) \ | |
aee3ad81 | 163 | ((flags) & IORESOURCE_DISABLED) |
1da177e4 | 164 | |
f5d94ff0 | 165 | int pnp_check_port(struct pnp_dev *dev, struct resource *res) |
1da177e4 | 166 | { |
ecfa935a | 167 | int i; |
1da177e4 | 168 | struct pnp_dev *tdev; |
f5d94ff0 | 169 | struct resource *tres; |
b60ba834 | 170 | resource_size_t *port, *end, *tport, *tend; |
07d4e9af | 171 | |
30c016a0 BH |
172 | port = &res->start; |
173 | end = &res->end; | |
1da177e4 LT |
174 | |
175 | /* if the resource doesn't exist, don't complain about it */ | |
30c016a0 | 176 | if (cannot_compare(res->flags)) |
1da177e4 LT |
177 | return 1; |
178 | ||
179 | /* check if the resource is already in use, skip if the | |
180 | * device is active because it itself may be in use */ | |
9dd78466 BH |
181 | if (!dev->active) { |
182 | if (__check_region(&ioport_resource, *port, length(port, end))) | |
1da177e4 LT |
183 | return 0; |
184 | } | |
185 | ||
186 | /* check if the resource is reserved */ | |
ecfa935a BH |
187 | for (i = 0; i < 8; i++) { |
188 | int rport = pnp_reserve_io[i << 1]; | |
189 | int rend = pnp_reserve_io[(i << 1) + 1] + rport - 1; | |
9dd78466 | 190 | if (ranged_conflict(port, end, &rport, &rend)) |
1da177e4 LT |
191 | return 0; |
192 | } | |
193 | ||
194 | /* check for internal conflicts */ | |
95ab3669 BH |
195 | for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) { |
196 | if (tres != res && tres->flags & IORESOURCE_IO) { | |
30c016a0 BH |
197 | tport = &tres->start; |
198 | tend = &tres->end; | |
9dd78466 | 199 | if (ranged_conflict(port, end, tport, tend)) |
1da177e4 LT |
200 | return 0; |
201 | } | |
202 | } | |
203 | ||
204 | /* check for conflicts with other pnp devices */ | |
205 | pnp_for_each_dev(tdev) { | |
206 | if (tdev == dev) | |
207 | continue; | |
95ab3669 BH |
208 | for (i = 0; |
209 | (tres = pnp_get_resource(tdev, IORESOURCE_IO, i)); | |
210 | i++) { | |
211 | if (tres->flags & IORESOURCE_IO) { | |
30c016a0 | 212 | if (cannot_compare(tres->flags)) |
1da177e4 | 213 | continue; |
30c016a0 BH |
214 | tport = &tres->start; |
215 | tend = &tres->end; | |
9dd78466 | 216 | if (ranged_conflict(port, end, tport, tend)) |
1da177e4 LT |
217 | return 0; |
218 | } | |
219 | } | |
220 | } | |
221 | ||
222 | return 1; | |
223 | } | |
224 | ||
f5d94ff0 | 225 | int pnp_check_mem(struct pnp_dev *dev, struct resource *res) |
1da177e4 | 226 | { |
ecfa935a | 227 | int i; |
1da177e4 | 228 | struct pnp_dev *tdev; |
f5d94ff0 | 229 | struct resource *tres; |
b60ba834 | 230 | resource_size_t *addr, *end, *taddr, *tend; |
07d4e9af | 231 | |
30c016a0 BH |
232 | addr = &res->start; |
233 | end = &res->end; | |
1da177e4 LT |
234 | |
235 | /* if the resource doesn't exist, don't complain about it */ | |
30c016a0 | 236 | if (cannot_compare(res->flags)) |
1da177e4 LT |
237 | return 1; |
238 | ||
239 | /* check if the resource is already in use, skip if the | |
240 | * device is active because it itself may be in use */ | |
9dd78466 BH |
241 | if (!dev->active) { |
242 | if (check_mem_region(*addr, length(addr, end))) | |
1da177e4 LT |
243 | return 0; |
244 | } | |
245 | ||
246 | /* check if the resource is reserved */ | |
ecfa935a BH |
247 | for (i = 0; i < 8; i++) { |
248 | int raddr = pnp_reserve_mem[i << 1]; | |
249 | int rend = pnp_reserve_mem[(i << 1) + 1] + raddr - 1; | |
9dd78466 | 250 | if (ranged_conflict(addr, end, &raddr, &rend)) |
1da177e4 LT |
251 | return 0; |
252 | } | |
253 | ||
254 | /* check for internal conflicts */ | |
95ab3669 BH |
255 | for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) { |
256 | if (tres != res && tres->flags & IORESOURCE_MEM) { | |
30c016a0 BH |
257 | taddr = &tres->start; |
258 | tend = &tres->end; | |
9dd78466 | 259 | if (ranged_conflict(addr, end, taddr, tend)) |
1da177e4 LT |
260 | return 0; |
261 | } | |
262 | } | |
263 | ||
264 | /* check for conflicts with other pnp devices */ | |
265 | pnp_for_each_dev(tdev) { | |
266 | if (tdev == dev) | |
267 | continue; | |
95ab3669 BH |
268 | for (i = 0; |
269 | (tres = pnp_get_resource(tdev, IORESOURCE_MEM, i)); | |
270 | i++) { | |
271 | if (tres->flags & IORESOURCE_MEM) { | |
30c016a0 | 272 | if (cannot_compare(tres->flags)) |
1da177e4 | 273 | continue; |
30c016a0 BH |
274 | taddr = &tres->start; |
275 | tend = &tres->end; | |
9dd78466 | 276 | if (ranged_conflict(addr, end, taddr, tend)) |
1da177e4 LT |
277 | return 0; |
278 | } | |
279 | } | |
280 | } | |
281 | ||
282 | return 1; | |
283 | } | |
284 | ||
7d12e780 | 285 | static irqreturn_t pnp_test_handler(int irq, void *dev_id) |
1da177e4 LT |
286 | { |
287 | return IRQ_HANDLED; | |
288 | } | |
289 | ||
84684c74 BH |
290 | #ifdef CONFIG_PCI |
291 | static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci, | |
292 | unsigned int irq) | |
293 | { | |
294 | u32 class; | |
295 | u8 progif; | |
296 | ||
297 | if (pci->irq == irq) { | |
2f53432c | 298 | pnp_dbg(&pnp->dev, " device %s using irq %d\n", |
84684c74 BH |
299 | pci_name(pci), irq); |
300 | return 1; | |
301 | } | |
302 | ||
303 | /* | |
304 | * See pci_setup_device() and ata_pci_sff_activate_host() for | |
305 | * similar IDE legacy detection. | |
306 | */ | |
307 | pci_read_config_dword(pci, PCI_CLASS_REVISION, &class); | |
308 | class >>= 8; /* discard revision ID */ | |
309 | progif = class & 0xff; | |
310 | class >>= 8; | |
311 | ||
312 | if (class == PCI_CLASS_STORAGE_IDE) { | |
313 | /* | |
314 | * Unless both channels are native-PCI mode only, | |
315 | * treat the compatibility IRQs as busy. | |
316 | */ | |
317 | if ((progif & 0x5) != 0x5) | |
318 | if (pci_get_legacy_ide_irq(pci, 0) == irq || | |
319 | pci_get_legacy_ide_irq(pci, 1) == irq) { | |
2f53432c | 320 | pnp_dbg(&pnp->dev, " legacy IDE device %s " |
84684c74 BH |
321 | "using irq %d\n", pci_name(pci), irq); |
322 | return 1; | |
323 | } | |
324 | } | |
325 | ||
326 | return 0; | |
327 | } | |
328 | #endif | |
329 | ||
330 | static int pci_uses_irq(struct pnp_dev *pnp, unsigned int irq) | |
331 | { | |
332 | #ifdef CONFIG_PCI | |
333 | struct pci_dev *pci = NULL; | |
334 | ||
335 | for_each_pci_dev(pci) { | |
336 | if (pci_dev_uses_irq(pnp, pci, irq)) { | |
337 | pci_dev_put(pci); | |
338 | return 1; | |
339 | } | |
340 | } | |
341 | #endif | |
342 | return 0; | |
343 | } | |
344 | ||
f5d94ff0 | 345 | int pnp_check_irq(struct pnp_dev *dev, struct resource *res) |
1da177e4 | 346 | { |
ecfa935a | 347 | int i; |
1da177e4 | 348 | struct pnp_dev *tdev; |
f5d94ff0 | 349 | struct resource *tres; |
30c016a0 BH |
350 | resource_size_t *irq; |
351 | ||
30c016a0 | 352 | irq = &res->start; |
1da177e4 LT |
353 | |
354 | /* if the resource doesn't exist, don't complain about it */ | |
30c016a0 | 355 | if (cannot_compare(res->flags)) |
1da177e4 LT |
356 | return 1; |
357 | ||
358 | /* check if the resource is valid */ | |
359 | if (*irq < 0 || *irq > 15) | |
360 | return 0; | |
361 | ||
362 | /* check if the resource is reserved */ | |
ecfa935a BH |
363 | for (i = 0; i < 16; i++) { |
364 | if (pnp_reserve_irq[i] == *irq) | |
1da177e4 LT |
365 | return 0; |
366 | } | |
367 | ||
368 | /* check for internal conflicts */ | |
95ab3669 BH |
369 | for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IRQ, i)); i++) { |
370 | if (tres != res && tres->flags & IORESOURCE_IRQ) { | |
30c016a0 | 371 | if (tres->start == *irq) |
1da177e4 LT |
372 | return 0; |
373 | } | |
374 | } | |
375 | ||
1da177e4 | 376 | /* check if the resource is being used by a pci device */ |
84684c74 BH |
377 | if (pci_uses_irq(dev, *irq)) |
378 | return 0; | |
1da177e4 LT |
379 | |
380 | /* check if the resource is already in use, skip if the | |
381 | * device is active because it itself may be in use */ | |
9dd78466 | 382 | if (!dev->active) { |
0cadaf45 | 383 | if (request_irq(*irq, pnp_test_handler, |
9dd78466 | 384 | IRQF_DISABLED | IRQF_PROBE_SHARED, "pnp", NULL)) |
1da177e4 LT |
385 | return 0; |
386 | free_irq(*irq, NULL); | |
387 | } | |
388 | ||
389 | /* check for conflicts with other pnp devices */ | |
390 | pnp_for_each_dev(tdev) { | |
391 | if (tdev == dev) | |
392 | continue; | |
95ab3669 BH |
393 | for (i = 0; |
394 | (tres = pnp_get_resource(tdev, IORESOURCE_IRQ, i)); | |
395 | i++) { | |
396 | if (tres->flags & IORESOURCE_IRQ) { | |
30c016a0 | 397 | if (cannot_compare(tres->flags)) |
1da177e4 | 398 | continue; |
30c016a0 | 399 | if (tres->start == *irq) |
1da177e4 LT |
400 | return 0; |
401 | } | |
402 | } | |
403 | } | |
404 | ||
405 | return 1; | |
406 | } | |
407 | ||
f5d94ff0 | 408 | int pnp_check_dma(struct pnp_dev *dev, struct resource *res) |
1da177e4 LT |
409 | { |
410 | #ifndef CONFIG_IA64 | |
ecfa935a | 411 | int i; |
1da177e4 | 412 | struct pnp_dev *tdev; |
f5d94ff0 | 413 | struct resource *tres; |
30c016a0 BH |
414 | resource_size_t *dma; |
415 | ||
30c016a0 | 416 | dma = &res->start; |
1da177e4 LT |
417 | |
418 | /* if the resource doesn't exist, don't complain about it */ | |
30c016a0 | 419 | if (cannot_compare(res->flags)) |
1da177e4 LT |
420 | return 1; |
421 | ||
422 | /* check if the resource is valid */ | |
423 | if (*dma < 0 || *dma == 4 || *dma > 7) | |
424 | return 0; | |
425 | ||
426 | /* check if the resource is reserved */ | |
ecfa935a BH |
427 | for (i = 0; i < 8; i++) { |
428 | if (pnp_reserve_dma[i] == *dma) | |
1da177e4 LT |
429 | return 0; |
430 | } | |
431 | ||
432 | /* check for internal conflicts */ | |
95ab3669 BH |
433 | for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_DMA, i)); i++) { |
434 | if (tres != res && tres->flags & IORESOURCE_DMA) { | |
30c016a0 | 435 | if (tres->start == *dma) |
1da177e4 LT |
436 | return 0; |
437 | } | |
438 | } | |
439 | ||
440 | /* check if the resource is already in use, skip if the | |
441 | * device is active because it itself may be in use */ | |
9dd78466 | 442 | if (!dev->active) { |
1da177e4 LT |
443 | if (request_dma(*dma, "pnp")) |
444 | return 0; | |
445 | free_dma(*dma); | |
446 | } | |
447 | ||
448 | /* check for conflicts with other pnp devices */ | |
449 | pnp_for_each_dev(tdev) { | |
450 | if (tdev == dev) | |
451 | continue; | |
95ab3669 BH |
452 | for (i = 0; |
453 | (tres = pnp_get_resource(tdev, IORESOURCE_DMA, i)); | |
454 | i++) { | |
455 | if (tres->flags & IORESOURCE_DMA) { | |
30c016a0 | 456 | if (cannot_compare(tres->flags)) |
1da177e4 | 457 | continue; |
30c016a0 | 458 | if (tres->start == *dma) |
1da177e4 LT |
459 | return 0; |
460 | } | |
461 | } | |
462 | } | |
463 | ||
464 | return 1; | |
465 | #else | |
07d4e9af | 466 | /* IA64 does not have legacy DMA */ |
1da177e4 LT |
467 | return 0; |
468 | #endif | |
469 | } | |
470 | ||
b563cf59 | 471 | unsigned long pnp_resource_type(struct resource *res) |
940e98db BH |
472 | { |
473 | return res->flags & (IORESOURCE_IO | IORESOURCE_MEM | | |
7e0e9c04 BH |
474 | IORESOURCE_IRQ | IORESOURCE_DMA | |
475 | IORESOURCE_BUS); | |
940e98db BH |
476 | } |
477 | ||
0a977f15 | 478 | struct resource *pnp_get_resource(struct pnp_dev *dev, |
b563cf59 | 479 | unsigned long type, unsigned int num) |
0a977f15 BH |
480 | { |
481 | struct pnp_resource *pnp_res; | |
aee3ad81 | 482 | struct resource *res; |
0a977f15 | 483 | |
aee3ad81 BH |
484 | list_for_each_entry(pnp_res, &dev->resources, list) { |
485 | res = &pnp_res->res; | |
486 | if (pnp_resource_type(res) == type && num-- == 0) | |
487 | return res; | |
488 | } | |
0a977f15 BH |
489 | return NULL; |
490 | } | |
b90eca0a BH |
491 | EXPORT_SYMBOL(pnp_get_resource); |
492 | ||
aee3ad81 | 493 | static struct pnp_resource *pnp_new_resource(struct pnp_dev *dev) |
a50b6d7b BH |
494 | { |
495 | struct pnp_resource *pnp_res; | |
a50b6d7b | 496 | |
aee3ad81 BH |
497 | pnp_res = kzalloc(sizeof(struct pnp_resource), GFP_KERNEL); |
498 | if (!pnp_res) | |
499 | return NULL; | |
500 | ||
501 | list_add_tail(&pnp_res->list, &dev->resources); | |
502 | return pnp_res; | |
a50b6d7b BH |
503 | } |
504 | ||
dbddd038 BH |
505 | struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, |
506 | int flags) | |
507 | { | |
508 | struct pnp_resource *pnp_res; | |
509 | struct resource *res; | |
dbddd038 | 510 | |
aee3ad81 | 511 | pnp_res = pnp_new_resource(dev); |
dbddd038 | 512 | if (!pnp_res) { |
25d39c39 | 513 | dev_err(&dev->dev, "can't add resource for IRQ %d\n", irq); |
dbddd038 BH |
514 | return NULL; |
515 | } | |
516 | ||
517 | res = &pnp_res->res; | |
518 | res->flags = IORESOURCE_IRQ | flags; | |
519 | res->start = irq; | |
520 | res->end = irq; | |
521 | ||
c7dabef8 | 522 | pnp_dbg(&dev->dev, " add %pr\n", res); |
dbddd038 BH |
523 | return pnp_res; |
524 | } | |
525 | ||
dc16f5f2 BH |
526 | struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma, |
527 | int flags) | |
528 | { | |
529 | struct pnp_resource *pnp_res; | |
530 | struct resource *res; | |
dc16f5f2 | 531 | |
aee3ad81 | 532 | pnp_res = pnp_new_resource(dev); |
dc16f5f2 | 533 | if (!pnp_res) { |
25d39c39 | 534 | dev_err(&dev->dev, "can't add resource for DMA %d\n", dma); |
dc16f5f2 BH |
535 | return NULL; |
536 | } | |
537 | ||
538 | res = &pnp_res->res; | |
539 | res->flags = IORESOURCE_DMA | flags; | |
540 | res->start = dma; | |
541 | res->end = dma; | |
542 | ||
c7dabef8 | 543 | pnp_dbg(&dev->dev, " add %pr\n", res); |
dc16f5f2 BH |
544 | return pnp_res; |
545 | } | |
546 | ||
cc8c2e30 BH |
547 | struct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev, |
548 | resource_size_t start, | |
549 | resource_size_t end, int flags) | |
550 | { | |
551 | struct pnp_resource *pnp_res; | |
552 | struct resource *res; | |
cc8c2e30 | 553 | |
aee3ad81 | 554 | pnp_res = pnp_new_resource(dev); |
cc8c2e30 | 555 | if (!pnp_res) { |
25d39c39 BH |
556 | dev_err(&dev->dev, "can't add resource for IO %#llx-%#llx\n", |
557 | (unsigned long long) start, | |
558 | (unsigned long long) end); | |
cc8c2e30 BH |
559 | return NULL; |
560 | } | |
561 | ||
562 | res = &pnp_res->res; | |
563 | res->flags = IORESOURCE_IO | flags; | |
564 | res->start = start; | |
565 | res->end = end; | |
566 | ||
c7dabef8 | 567 | pnp_dbg(&dev->dev, " add %pr\n", res); |
cc8c2e30 BH |
568 | return pnp_res; |
569 | } | |
570 | ||
d6180f36 BH |
571 | struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, |
572 | resource_size_t start, | |
573 | resource_size_t end, int flags) | |
574 | { | |
575 | struct pnp_resource *pnp_res; | |
576 | struct resource *res; | |
d6180f36 | 577 | |
aee3ad81 | 578 | pnp_res = pnp_new_resource(dev); |
d6180f36 | 579 | if (!pnp_res) { |
25d39c39 BH |
580 | dev_err(&dev->dev, "can't add resource for MEM %#llx-%#llx\n", |
581 | (unsigned long long) start, | |
582 | (unsigned long long) end); | |
d6180f36 BH |
583 | return NULL; |
584 | } | |
585 | ||
586 | res = &pnp_res->res; | |
587 | res->flags = IORESOURCE_MEM | flags; | |
588 | res->start = start; | |
589 | res->end = end; | |
590 | ||
c7dabef8 | 591 | pnp_dbg(&dev->dev, " add %pr\n", res); |
d6180f36 BH |
592 | return pnp_res; |
593 | } | |
594 | ||
7e0e9c04 BH |
595 | struct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev, |
596 | resource_size_t start, | |
597 | resource_size_t end) | |
598 | { | |
599 | struct pnp_resource *pnp_res; | |
600 | struct resource *res; | |
601 | ||
602 | pnp_res = pnp_new_resource(dev); | |
603 | if (!pnp_res) { | |
604 | dev_err(&dev->dev, "can't add resource for BUS %#llx-%#llx\n", | |
605 | (unsigned long long) start, | |
606 | (unsigned long long) end); | |
607 | return NULL; | |
608 | } | |
609 | ||
610 | res = &pnp_res->res; | |
611 | res->flags = IORESOURCE_BUS; | |
612 | res->start = start; | |
613 | res->end = end; | |
614 | ||
615 | pnp_dbg(&dev->dev, " add %pr\n", res); | |
616 | return pnp_res; | |
617 | } | |
618 | ||
1f32ca31 BH |
619 | /* |
620 | * Determine whether the specified resource is a possible configuration | |
621 | * for this device. | |
622 | */ | |
623 | int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start, | |
624 | resource_size_t size) | |
57fd51a8 | 625 | { |
1f32ca31 | 626 | struct pnp_option *option; |
57fd51a8 BH |
627 | struct pnp_port *port; |
628 | struct pnp_mem *mem; | |
629 | struct pnp_irq *irq; | |
630 | struct pnp_dma *dma; | |
631 | ||
1f32ca31 BH |
632 | list_for_each_entry(option, &dev->options, list) { |
633 | if (option->type != type) | |
634 | continue; | |
57fd51a8 | 635 | |
1f32ca31 | 636 | switch (option->type) { |
57fd51a8 | 637 | case IORESOURCE_IO: |
1f32ca31 BH |
638 | port = &option->u.port; |
639 | if (port->min == start && port->size == size) | |
640 | return 1; | |
57fd51a8 BH |
641 | break; |
642 | case IORESOURCE_MEM: | |
1f32ca31 BH |
643 | mem = &option->u.mem; |
644 | if (mem->min == start && mem->size == size) | |
645 | return 1; | |
57fd51a8 BH |
646 | break; |
647 | case IORESOURCE_IRQ: | |
1f32ca31 BH |
648 | irq = &option->u.irq; |
649 | if (start < PNP_IRQ_NR && | |
650 | test_bit(start, irq->map.bits)) | |
651 | return 1; | |
57fd51a8 BH |
652 | break; |
653 | case IORESOURCE_DMA: | |
1f32ca31 BH |
654 | dma = &option->u.dma; |
655 | if (dma->map & (1 << start)) | |
656 | return 1; | |
57fd51a8 BH |
657 | break; |
658 | } | |
659 | } | |
660 | ||
661 | return 0; | |
662 | } | |
57fd51a8 BH |
663 | EXPORT_SYMBOL(pnp_possible_config); |
664 | ||
1b8e6966 BH |
665 | int pnp_range_reserved(resource_size_t start, resource_size_t end) |
666 | { | |
667 | struct pnp_dev *dev; | |
668 | struct pnp_resource *pnp_res; | |
669 | resource_size_t *dev_start, *dev_end; | |
670 | ||
671 | pnp_for_each_dev(dev) { | |
672 | list_for_each_entry(pnp_res, &dev->resources, list) { | |
673 | dev_start = &pnp_res->res.start; | |
674 | dev_end = &pnp_res->res.end; | |
675 | if (ranged_conflict(&start, &end, dev_start, dev_end)) | |
676 | return 1; | |
677 | } | |
678 | } | |
679 | return 0; | |
680 | } | |
681 | EXPORT_SYMBOL(pnp_range_reserved); | |
682 | ||
1da177e4 | 683 | /* format is: pnp_reserve_irq=irq1[,irq2] .... */ |
1da177e4 LT |
684 | static int __init pnp_setup_reserve_irq(char *str) |
685 | { | |
686 | int i; | |
687 | ||
688 | for (i = 0; i < 16; i++) | |
9dd78466 | 689 | if (get_option(&str, &pnp_reserve_irq[i]) != 2) |
1da177e4 LT |
690 | break; |
691 | return 1; | |
692 | } | |
693 | ||
694 | __setup("pnp_reserve_irq=", pnp_setup_reserve_irq); | |
695 | ||
696 | /* format is: pnp_reserve_dma=dma1[,dma2] .... */ | |
1da177e4 LT |
697 | static int __init pnp_setup_reserve_dma(char *str) |
698 | { | |
699 | int i; | |
700 | ||
701 | for (i = 0; i < 8; i++) | |
9dd78466 | 702 | if (get_option(&str, &pnp_reserve_dma[i]) != 2) |
1da177e4 LT |
703 | break; |
704 | return 1; | |
705 | } | |
706 | ||
707 | __setup("pnp_reserve_dma=", pnp_setup_reserve_dma); | |
708 | ||
709 | /* format is: pnp_reserve_io=io1,size1[,io2,size2] .... */ | |
1da177e4 LT |
710 | static int __init pnp_setup_reserve_io(char *str) |
711 | { | |
712 | int i; | |
713 | ||
714 | for (i = 0; i < 16; i++) | |
9dd78466 | 715 | if (get_option(&str, &pnp_reserve_io[i]) != 2) |
1da177e4 LT |
716 | break; |
717 | return 1; | |
718 | } | |
719 | ||
720 | __setup("pnp_reserve_io=", pnp_setup_reserve_io); | |
721 | ||
722 | /* format is: pnp_reserve_mem=mem1,size1[,mem2,size2] .... */ | |
1da177e4 LT |
723 | static int __init pnp_setup_reserve_mem(char *str) |
724 | { | |
725 | int i; | |
726 | ||
727 | for (i = 0; i < 16; i++) | |
9dd78466 | 728 | if (get_option(&str, &pnp_reserve_mem[i]) != 2) |
1da177e4 LT |
729 | break; |
730 | return 1; | |
731 | } | |
732 | ||
733 | __setup("pnp_reserve_mem=", pnp_setup_reserve_mem); |