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