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