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 | ||
62c6dae0 | 34 | static struct pnp_option *pnp_build_option(struct pnp_dev *dev, unsigned long type, |
1f32ca31 | 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 | 181 | if (!dev->active) { |
eeeb98bf | 182 | if (!request_region(*port, length(port, end), "pnp")) |
1da177e4 | 183 | return 0; |
eeeb98bf | 184 | release_region(*port, length(port, end)); |
1da177e4 LT |
185 | } |
186 | ||
187 | /* check if the resource is reserved */ | |
ecfa935a BH |
188 | for (i = 0; i < 8; i++) { |
189 | int rport = pnp_reserve_io[i << 1]; | |
190 | int rend = pnp_reserve_io[(i << 1) + 1] + rport - 1; | |
9dd78466 | 191 | if (ranged_conflict(port, end, &rport, &rend)) |
1da177e4 LT |
192 | return 0; |
193 | } | |
194 | ||
195 | /* check for internal conflicts */ | |
95ab3669 BH |
196 | for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IO, i)); i++) { |
197 | if (tres != res && tres->flags & IORESOURCE_IO) { | |
30c016a0 BH |
198 | tport = &tres->start; |
199 | tend = &tres->end; | |
9dd78466 | 200 | if (ranged_conflict(port, end, tport, tend)) |
1da177e4 LT |
201 | return 0; |
202 | } | |
203 | } | |
204 | ||
205 | /* check for conflicts with other pnp devices */ | |
206 | pnp_for_each_dev(tdev) { | |
207 | if (tdev == dev) | |
208 | continue; | |
95ab3669 BH |
209 | for (i = 0; |
210 | (tres = pnp_get_resource(tdev, IORESOURCE_IO, i)); | |
211 | i++) { | |
212 | if (tres->flags & IORESOURCE_IO) { | |
30c016a0 | 213 | if (cannot_compare(tres->flags)) |
1da177e4 | 214 | continue; |
11439a6f BH |
215 | if (tres->flags & IORESOURCE_WINDOW) |
216 | continue; | |
30c016a0 BH |
217 | tport = &tres->start; |
218 | tend = &tres->end; | |
9dd78466 | 219 | if (ranged_conflict(port, end, tport, tend)) |
1da177e4 LT |
220 | return 0; |
221 | } | |
222 | } | |
223 | } | |
224 | ||
225 | return 1; | |
226 | } | |
227 | ||
f5d94ff0 | 228 | int pnp_check_mem(struct pnp_dev *dev, struct resource *res) |
1da177e4 | 229 | { |
ecfa935a | 230 | int i; |
1da177e4 | 231 | struct pnp_dev *tdev; |
f5d94ff0 | 232 | struct resource *tres; |
b60ba834 | 233 | resource_size_t *addr, *end, *taddr, *tend; |
07d4e9af | 234 | |
30c016a0 BH |
235 | addr = &res->start; |
236 | end = &res->end; | |
1da177e4 LT |
237 | |
238 | /* if the resource doesn't exist, don't complain about it */ | |
30c016a0 | 239 | if (cannot_compare(res->flags)) |
1da177e4 LT |
240 | return 1; |
241 | ||
242 | /* check if the resource is already in use, skip if the | |
243 | * device is active because it itself may be in use */ | |
9dd78466 | 244 | if (!dev->active) { |
eeeb98bf | 245 | if (!request_mem_region(*addr, length(addr, end), "pnp")) |
1da177e4 | 246 | return 0; |
eeeb98bf | 247 | release_mem_region(*addr, length(addr, end)); |
1da177e4 LT |
248 | } |
249 | ||
250 | /* check if the resource is reserved */ | |
ecfa935a BH |
251 | for (i = 0; i < 8; i++) { |
252 | int raddr = pnp_reserve_mem[i << 1]; | |
253 | int rend = pnp_reserve_mem[(i << 1) + 1] + raddr - 1; | |
9dd78466 | 254 | if (ranged_conflict(addr, end, &raddr, &rend)) |
1da177e4 LT |
255 | return 0; |
256 | } | |
257 | ||
258 | /* check for internal conflicts */ | |
95ab3669 BH |
259 | for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) { |
260 | if (tres != res && tres->flags & IORESOURCE_MEM) { | |
30c016a0 BH |
261 | taddr = &tres->start; |
262 | tend = &tres->end; | |
9dd78466 | 263 | if (ranged_conflict(addr, end, taddr, tend)) |
1da177e4 LT |
264 | return 0; |
265 | } | |
266 | } | |
267 | ||
268 | /* check for conflicts with other pnp devices */ | |
269 | pnp_for_each_dev(tdev) { | |
270 | if (tdev == dev) | |
271 | continue; | |
95ab3669 BH |
272 | for (i = 0; |
273 | (tres = pnp_get_resource(tdev, IORESOURCE_MEM, i)); | |
274 | i++) { | |
275 | if (tres->flags & IORESOURCE_MEM) { | |
30c016a0 | 276 | if (cannot_compare(tres->flags)) |
1da177e4 | 277 | continue; |
11439a6f BH |
278 | if (tres->flags & IORESOURCE_WINDOW) |
279 | continue; | |
30c016a0 BH |
280 | taddr = &tres->start; |
281 | tend = &tres->end; | |
9dd78466 | 282 | if (ranged_conflict(addr, end, taddr, tend)) |
1da177e4 LT |
283 | return 0; |
284 | } | |
285 | } | |
286 | } | |
287 | ||
288 | return 1; | |
289 | } | |
290 | ||
7d12e780 | 291 | static irqreturn_t pnp_test_handler(int irq, void *dev_id) |
1da177e4 LT |
292 | { |
293 | return IRQ_HANDLED; | |
294 | } | |
295 | ||
84684c74 BH |
296 | #ifdef CONFIG_PCI |
297 | static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci, | |
298 | unsigned int irq) | |
299 | { | |
300 | u32 class; | |
301 | u8 progif; | |
302 | ||
303 | if (pci->irq == irq) { | |
2f53432c | 304 | pnp_dbg(&pnp->dev, " device %s using irq %d\n", |
84684c74 BH |
305 | pci_name(pci), irq); |
306 | return 1; | |
307 | } | |
308 | ||
309 | /* | |
310 | * See pci_setup_device() and ata_pci_sff_activate_host() for | |
311 | * similar IDE legacy detection. | |
312 | */ | |
313 | pci_read_config_dword(pci, PCI_CLASS_REVISION, &class); | |
314 | class >>= 8; /* discard revision ID */ | |
315 | progif = class & 0xff; | |
316 | class >>= 8; | |
317 | ||
318 | if (class == PCI_CLASS_STORAGE_IDE) { | |
319 | /* | |
320 | * Unless both channels are native-PCI mode only, | |
321 | * treat the compatibility IRQs as busy. | |
322 | */ | |
323 | if ((progif & 0x5) != 0x5) | |
324 | if (pci_get_legacy_ide_irq(pci, 0) == irq || | |
325 | pci_get_legacy_ide_irq(pci, 1) == irq) { | |
2f53432c | 326 | pnp_dbg(&pnp->dev, " legacy IDE device %s " |
84684c74 BH |
327 | "using irq %d\n", pci_name(pci), irq); |
328 | return 1; | |
329 | } | |
330 | } | |
331 | ||
332 | return 0; | |
333 | } | |
334 | #endif | |
335 | ||
336 | static int pci_uses_irq(struct pnp_dev *pnp, unsigned int irq) | |
337 | { | |
338 | #ifdef CONFIG_PCI | |
339 | struct pci_dev *pci = NULL; | |
340 | ||
341 | for_each_pci_dev(pci) { | |
342 | if (pci_dev_uses_irq(pnp, pci, irq)) { | |
343 | pci_dev_put(pci); | |
344 | return 1; | |
345 | } | |
346 | } | |
347 | #endif | |
348 | return 0; | |
349 | } | |
350 | ||
f5d94ff0 | 351 | int pnp_check_irq(struct pnp_dev *dev, struct resource *res) |
1da177e4 | 352 | { |
ecfa935a | 353 | int i; |
1da177e4 | 354 | struct pnp_dev *tdev; |
f5d94ff0 | 355 | struct resource *tres; |
30c016a0 BH |
356 | resource_size_t *irq; |
357 | ||
30c016a0 | 358 | irq = &res->start; |
1da177e4 LT |
359 | |
360 | /* if the resource doesn't exist, don't complain about it */ | |
30c016a0 | 361 | if (cannot_compare(res->flags)) |
1da177e4 LT |
362 | return 1; |
363 | ||
364 | /* check if the resource is valid */ | |
ff73b80d | 365 | if (*irq > 15) |
1da177e4 LT |
366 | return 0; |
367 | ||
368 | /* check if the resource is reserved */ | |
ecfa935a BH |
369 | for (i = 0; i < 16; i++) { |
370 | if (pnp_reserve_irq[i] == *irq) | |
1da177e4 LT |
371 | return 0; |
372 | } | |
373 | ||
374 | /* check for internal conflicts */ | |
95ab3669 BH |
375 | for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_IRQ, i)); i++) { |
376 | if (tres != res && tres->flags & IORESOURCE_IRQ) { | |
30c016a0 | 377 | if (tres->start == *irq) |
1da177e4 LT |
378 | return 0; |
379 | } | |
380 | } | |
381 | ||
1da177e4 | 382 | /* check if the resource is being used by a pci device */ |
84684c74 BH |
383 | if (pci_uses_irq(dev, *irq)) |
384 | return 0; | |
1da177e4 LT |
385 | |
386 | /* check if the resource is already in use, skip if the | |
387 | * device is active because it itself may be in use */ | |
9dd78466 | 388 | if (!dev->active) { |
0cadaf45 | 389 | if (request_irq(*irq, pnp_test_handler, |
e1563769 | 390 | IRQF_PROBE_SHARED, "pnp", NULL)) |
1da177e4 LT |
391 | return 0; |
392 | free_irq(*irq, NULL); | |
393 | } | |
394 | ||
395 | /* check for conflicts with other pnp devices */ | |
396 | pnp_for_each_dev(tdev) { | |
397 | if (tdev == dev) | |
398 | continue; | |
95ab3669 BH |
399 | for (i = 0; |
400 | (tres = pnp_get_resource(tdev, IORESOURCE_IRQ, i)); | |
401 | i++) { | |
402 | if (tres->flags & IORESOURCE_IRQ) { | |
30c016a0 | 403 | if (cannot_compare(tres->flags)) |
1da177e4 | 404 | continue; |
30c016a0 | 405 | if (tres->start == *irq) |
1da177e4 LT |
406 | return 0; |
407 | } | |
408 | } | |
409 | } | |
410 | ||
411 | return 1; | |
412 | } | |
413 | ||
586f83e2 | 414 | #ifdef CONFIG_ISA_DMA_API |
f5d94ff0 | 415 | int pnp_check_dma(struct pnp_dev *dev, struct resource *res) |
1da177e4 | 416 | { |
ecfa935a | 417 | int i; |
1da177e4 | 418 | struct pnp_dev *tdev; |
f5d94ff0 | 419 | struct resource *tres; |
30c016a0 BH |
420 | resource_size_t *dma; |
421 | ||
30c016a0 | 422 | dma = &res->start; |
1da177e4 LT |
423 | |
424 | /* if the resource doesn't exist, don't complain about it */ | |
30c016a0 | 425 | if (cannot_compare(res->flags)) |
1da177e4 LT |
426 | return 1; |
427 | ||
428 | /* check if the resource is valid */ | |
ff73b80d | 429 | if (*dma == 4 || *dma > 7) |
1da177e4 LT |
430 | return 0; |
431 | ||
432 | /* check if the resource is reserved */ | |
ecfa935a BH |
433 | for (i = 0; i < 8; i++) { |
434 | if (pnp_reserve_dma[i] == *dma) | |
1da177e4 LT |
435 | return 0; |
436 | } | |
437 | ||
438 | /* check for internal conflicts */ | |
95ab3669 BH |
439 | for (i = 0; (tres = pnp_get_resource(dev, IORESOURCE_DMA, i)); i++) { |
440 | if (tres != res && tres->flags & IORESOURCE_DMA) { | |
30c016a0 | 441 | if (tres->start == *dma) |
1da177e4 LT |
442 | return 0; |
443 | } | |
444 | } | |
445 | ||
446 | /* check if the resource is already in use, skip if the | |
447 | * device is active because it itself may be in use */ | |
9dd78466 | 448 | if (!dev->active) { |
1da177e4 LT |
449 | if (request_dma(*dma, "pnp")) |
450 | return 0; | |
451 | free_dma(*dma); | |
452 | } | |
453 | ||
454 | /* check for conflicts with other pnp devices */ | |
455 | pnp_for_each_dev(tdev) { | |
456 | if (tdev == dev) | |
457 | continue; | |
95ab3669 BH |
458 | for (i = 0; |
459 | (tres = pnp_get_resource(tdev, IORESOURCE_DMA, i)); | |
460 | i++) { | |
461 | if (tres->flags & IORESOURCE_DMA) { | |
30c016a0 | 462 | if (cannot_compare(tres->flags)) |
1da177e4 | 463 | continue; |
30c016a0 | 464 | if (tres->start == *dma) |
1da177e4 LT |
465 | return 0; |
466 | } | |
467 | } | |
468 | } | |
469 | ||
470 | return 1; | |
1da177e4 | 471 | } |
586f83e2 | 472 | #endif /* CONFIG_ISA_DMA_API */ |
1da177e4 | 473 | |
b563cf59 | 474 | unsigned long pnp_resource_type(struct resource *res) |
940e98db BH |
475 | { |
476 | return res->flags & (IORESOURCE_IO | IORESOURCE_MEM | | |
7e0e9c04 BH |
477 | IORESOURCE_IRQ | IORESOURCE_DMA | |
478 | IORESOURCE_BUS); | |
940e98db BH |
479 | } |
480 | ||
0a977f15 | 481 | struct resource *pnp_get_resource(struct pnp_dev *dev, |
b563cf59 | 482 | unsigned long type, unsigned int num) |
0a977f15 BH |
483 | { |
484 | struct pnp_resource *pnp_res; | |
aee3ad81 | 485 | struct resource *res; |
0a977f15 | 486 | |
aee3ad81 BH |
487 | list_for_each_entry(pnp_res, &dev->resources, list) { |
488 | res = &pnp_res->res; | |
489 | if (pnp_resource_type(res) == type && num-- == 0) | |
490 | return res; | |
491 | } | |
0a977f15 BH |
492 | return NULL; |
493 | } | |
b90eca0a BH |
494 | EXPORT_SYMBOL(pnp_get_resource); |
495 | ||
aee3ad81 | 496 | static struct pnp_resource *pnp_new_resource(struct pnp_dev *dev) |
a50b6d7b BH |
497 | { |
498 | struct pnp_resource *pnp_res; | |
a50b6d7b | 499 | |
aee3ad81 BH |
500 | pnp_res = kzalloc(sizeof(struct pnp_resource), GFP_KERNEL); |
501 | if (!pnp_res) | |
502 | return NULL; | |
503 | ||
504 | list_add_tail(&pnp_res->list, &dev->resources); | |
505 | return pnp_res; | |
a50b6d7b BH |
506 | } |
507 | ||
046d9ce6 RW |
508 | struct pnp_resource *pnp_add_resource(struct pnp_dev *dev, |
509 | struct resource *res) | |
510 | { | |
511 | struct pnp_resource *pnp_res; | |
512 | ||
513 | pnp_res = pnp_new_resource(dev); | |
514 | if (!pnp_res) { | |
515 | dev_err(&dev->dev, "can't add resource %pR\n", res); | |
516 | return NULL; | |
517 | } | |
518 | ||
519 | pnp_res->res = *res; | |
3c0fc071 | 520 | pnp_res->res.name = dev->name; |
046d9ce6 RW |
521 | dev_dbg(&dev->dev, "%pR\n", res); |
522 | return pnp_res; | |
523 | } | |
524 | ||
dbddd038 BH |
525 | struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, |
526 | int flags) | |
527 | { | |
528 | struct pnp_resource *pnp_res; | |
529 | struct resource *res; | |
dbddd038 | 530 | |
aee3ad81 | 531 | pnp_res = pnp_new_resource(dev); |
dbddd038 | 532 | if (!pnp_res) { |
25d39c39 | 533 | dev_err(&dev->dev, "can't add resource for IRQ %d\n", irq); |
dbddd038 BH |
534 | return NULL; |
535 | } | |
536 | ||
537 | res = &pnp_res->res; | |
538 | res->flags = IORESOURCE_IRQ | flags; | |
539 | res->start = irq; | |
540 | res->end = irq; | |
541 | ||
c1f3f281 | 542 | dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); |
dbddd038 BH |
543 | return pnp_res; |
544 | } | |
545 | ||
dc16f5f2 BH |
546 | struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma, |
547 | int flags) | |
548 | { | |
549 | struct pnp_resource *pnp_res; | |
550 | struct resource *res; | |
dc16f5f2 | 551 | |
aee3ad81 | 552 | pnp_res = pnp_new_resource(dev); |
dc16f5f2 | 553 | if (!pnp_res) { |
25d39c39 | 554 | dev_err(&dev->dev, "can't add resource for DMA %d\n", dma); |
dc16f5f2 BH |
555 | return NULL; |
556 | } | |
557 | ||
558 | res = &pnp_res->res; | |
559 | res->flags = IORESOURCE_DMA | flags; | |
560 | res->start = dma; | |
561 | res->end = dma; | |
562 | ||
c1f3f281 | 563 | dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); |
dc16f5f2 BH |
564 | return pnp_res; |
565 | } | |
566 | ||
cc8c2e30 BH |
567 | struct pnp_resource *pnp_add_io_resource(struct pnp_dev *dev, |
568 | resource_size_t start, | |
569 | resource_size_t end, int flags) | |
570 | { | |
571 | struct pnp_resource *pnp_res; | |
572 | struct resource *res; | |
cc8c2e30 | 573 | |
aee3ad81 | 574 | pnp_res = pnp_new_resource(dev); |
cc8c2e30 | 575 | if (!pnp_res) { |
25d39c39 BH |
576 | dev_err(&dev->dev, "can't add resource for IO %#llx-%#llx\n", |
577 | (unsigned long long) start, | |
578 | (unsigned long long) end); | |
cc8c2e30 BH |
579 | return NULL; |
580 | } | |
581 | ||
582 | res = &pnp_res->res; | |
583 | res->flags = IORESOURCE_IO | flags; | |
584 | res->start = start; | |
585 | res->end = end; | |
586 | ||
c1f3f281 | 587 | dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); |
cc8c2e30 BH |
588 | return pnp_res; |
589 | } | |
590 | ||
d6180f36 BH |
591 | struct pnp_resource *pnp_add_mem_resource(struct pnp_dev *dev, |
592 | resource_size_t start, | |
593 | resource_size_t end, int flags) | |
594 | { | |
595 | struct pnp_resource *pnp_res; | |
596 | struct resource *res; | |
d6180f36 | 597 | |
aee3ad81 | 598 | pnp_res = pnp_new_resource(dev); |
d6180f36 | 599 | if (!pnp_res) { |
25d39c39 BH |
600 | dev_err(&dev->dev, "can't add resource for MEM %#llx-%#llx\n", |
601 | (unsigned long long) start, | |
602 | (unsigned long long) end); | |
d6180f36 BH |
603 | return NULL; |
604 | } | |
605 | ||
606 | res = &pnp_res->res; | |
607 | res->flags = IORESOURCE_MEM | flags; | |
608 | res->start = start; | |
609 | res->end = end; | |
610 | ||
c1f3f281 | 611 | dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); |
d6180f36 BH |
612 | return pnp_res; |
613 | } | |
614 | ||
7e0e9c04 BH |
615 | struct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev, |
616 | resource_size_t start, | |
617 | resource_size_t end) | |
618 | { | |
619 | struct pnp_resource *pnp_res; | |
620 | struct resource *res; | |
621 | ||
622 | pnp_res = pnp_new_resource(dev); | |
623 | if (!pnp_res) { | |
624 | dev_err(&dev->dev, "can't add resource for BUS %#llx-%#llx\n", | |
625 | (unsigned long long) start, | |
626 | (unsigned long long) end); | |
627 | return NULL; | |
628 | } | |
629 | ||
630 | res = &pnp_res->res; | |
631 | res->flags = IORESOURCE_BUS; | |
632 | res->start = start; | |
633 | res->end = end; | |
634 | ||
c1f3f281 | 635 | dev_printk(KERN_DEBUG, &dev->dev, "%pR\n", res); |
7e0e9c04 BH |
636 | return pnp_res; |
637 | } | |
638 | ||
1f32ca31 BH |
639 | /* |
640 | * Determine whether the specified resource is a possible configuration | |
641 | * for this device. | |
642 | */ | |
643 | int pnp_possible_config(struct pnp_dev *dev, int type, resource_size_t start, | |
644 | resource_size_t size) | |
57fd51a8 | 645 | { |
1f32ca31 | 646 | struct pnp_option *option; |
57fd51a8 BH |
647 | struct pnp_port *port; |
648 | struct pnp_mem *mem; | |
649 | struct pnp_irq *irq; | |
650 | struct pnp_dma *dma; | |
651 | ||
1f32ca31 BH |
652 | list_for_each_entry(option, &dev->options, list) { |
653 | if (option->type != type) | |
654 | continue; | |
57fd51a8 | 655 | |
1f32ca31 | 656 | switch (option->type) { |
57fd51a8 | 657 | case IORESOURCE_IO: |
1f32ca31 BH |
658 | port = &option->u.port; |
659 | if (port->min == start && port->size == size) | |
660 | return 1; | |
57fd51a8 BH |
661 | break; |
662 | case IORESOURCE_MEM: | |
1f32ca31 BH |
663 | mem = &option->u.mem; |
664 | if (mem->min == start && mem->size == size) | |
665 | return 1; | |
57fd51a8 BH |
666 | break; |
667 | case IORESOURCE_IRQ: | |
1f32ca31 BH |
668 | irq = &option->u.irq; |
669 | if (start < PNP_IRQ_NR && | |
670 | test_bit(start, irq->map.bits)) | |
671 | return 1; | |
57fd51a8 BH |
672 | break; |
673 | case IORESOURCE_DMA: | |
1f32ca31 BH |
674 | dma = &option->u.dma; |
675 | if (dma->map & (1 << start)) | |
676 | return 1; | |
57fd51a8 BH |
677 | break; |
678 | } | |
679 | } | |
680 | ||
681 | return 0; | |
682 | } | |
57fd51a8 BH |
683 | EXPORT_SYMBOL(pnp_possible_config); |
684 | ||
1b8e6966 BH |
685 | int pnp_range_reserved(resource_size_t start, resource_size_t end) |
686 | { | |
687 | struct pnp_dev *dev; | |
688 | struct pnp_resource *pnp_res; | |
689 | resource_size_t *dev_start, *dev_end; | |
690 | ||
691 | pnp_for_each_dev(dev) { | |
692 | list_for_each_entry(pnp_res, &dev->resources, list) { | |
693 | dev_start = &pnp_res->res.start; | |
694 | dev_end = &pnp_res->res.end; | |
695 | if (ranged_conflict(&start, &end, dev_start, dev_end)) | |
696 | return 1; | |
697 | } | |
698 | } | |
699 | return 0; | |
700 | } | |
701 | EXPORT_SYMBOL(pnp_range_reserved); | |
702 | ||
1da177e4 | 703 | /* format is: pnp_reserve_irq=irq1[,irq2] .... */ |
1da177e4 LT |
704 | static int __init pnp_setup_reserve_irq(char *str) |
705 | { | |
706 | int i; | |
707 | ||
708 | for (i = 0; i < 16; i++) | |
9dd78466 | 709 | if (get_option(&str, &pnp_reserve_irq[i]) != 2) |
1da177e4 LT |
710 | break; |
711 | return 1; | |
712 | } | |
713 | ||
714 | __setup("pnp_reserve_irq=", pnp_setup_reserve_irq); | |
715 | ||
716 | /* format is: pnp_reserve_dma=dma1[,dma2] .... */ | |
1da177e4 LT |
717 | static int __init pnp_setup_reserve_dma(char *str) |
718 | { | |
719 | int i; | |
720 | ||
721 | for (i = 0; i < 8; i++) | |
9dd78466 | 722 | if (get_option(&str, &pnp_reserve_dma[i]) != 2) |
1da177e4 LT |
723 | break; |
724 | return 1; | |
725 | } | |
726 | ||
727 | __setup("pnp_reserve_dma=", pnp_setup_reserve_dma); | |
728 | ||
729 | /* format is: pnp_reserve_io=io1,size1[,io2,size2] .... */ | |
1da177e4 LT |
730 | static int __init pnp_setup_reserve_io(char *str) |
731 | { | |
732 | int i; | |
733 | ||
734 | for (i = 0; i < 16; i++) | |
9dd78466 | 735 | if (get_option(&str, &pnp_reserve_io[i]) != 2) |
1da177e4 LT |
736 | break; |
737 | return 1; | |
738 | } | |
739 | ||
740 | __setup("pnp_reserve_io=", pnp_setup_reserve_io); | |
741 | ||
742 | /* format is: pnp_reserve_mem=mem1,size1[,mem2,size2] .... */ | |
1da177e4 LT |
743 | static int __init pnp_setup_reserve_mem(char *str) |
744 | { | |
745 | int i; | |
746 | ||
747 | for (i = 0; i < 16; i++) | |
9dd78466 | 748 | if (get_option(&str, &pnp_reserve_mem[i]) != 2) |
1da177e4 LT |
749 | break; |
750 | return 1; | |
751 | } | |
752 | ||
753 | __setup("pnp_reserve_mem=", pnp_setup_reserve_mem); |