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