diff options
Diffstat (limited to 'arch/powerpc/kernel/irq.c')
-rw-r--r-- | arch/powerpc/kernel/irq.c | 629 |
1 files changed, 507 insertions, 122 deletions
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 91248559099a..05a700940f67 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
@@ -29,6 +29,8 @@ | |||
29 | * to reduce code space and undefined function references. | 29 | * to reduce code space and undefined function references. |
30 | */ | 30 | */ |
31 | 31 | ||
32 | #undef DEBUG | ||
33 | |||
32 | #include <linux/module.h> | 34 | #include <linux/module.h> |
33 | #include <linux/threads.h> | 35 | #include <linux/threads.h> |
34 | #include <linux/kernel_stat.h> | 36 | #include <linux/kernel_stat.h> |
@@ -46,7 +48,10 @@ | |||
46 | #include <linux/cpumask.h> | 48 | #include <linux/cpumask.h> |
47 | #include <linux/profile.h> | 49 | #include <linux/profile.h> |
48 | #include <linux/bitops.h> | 50 | #include <linux/bitops.h> |
49 | #include <linux/pci.h> | 51 | #include <linux/list.h> |
52 | #include <linux/radix-tree.h> | ||
53 | #include <linux/mutex.h> | ||
54 | #include <linux/bootmem.h> | ||
50 | 55 | ||
51 | #include <asm/uaccess.h> | 56 | #include <asm/uaccess.h> |
52 | #include <asm/system.h> | 57 | #include <asm/system.h> |
@@ -57,6 +62,7 @@ | |||
57 | #include <asm/prom.h> | 62 | #include <asm/prom.h> |
58 | #include <asm/ptrace.h> | 63 | #include <asm/ptrace.h> |
59 | #include <asm/machdep.h> | 64 | #include <asm/machdep.h> |
65 | #include <asm/udbg.h> | ||
60 | #ifdef CONFIG_PPC_ISERIES | 66 | #ifdef CONFIG_PPC_ISERIES |
61 | #include <asm/paca.h> | 67 | #include <asm/paca.h> |
62 | #endif | 68 | #endif |
@@ -88,7 +94,6 @@ extern atomic_t ipi_sent; | |||
88 | EXPORT_SYMBOL(irq_desc); | 94 | EXPORT_SYMBOL(irq_desc); |
89 | 95 | ||
90 | int distribute_irqs = 1; | 96 | int distribute_irqs = 1; |
91 | u64 ppc64_interrupt_controller; | ||
92 | #endif /* CONFIG_PPC64 */ | 97 | #endif /* CONFIG_PPC64 */ |
93 | 98 | ||
94 | int show_interrupts(struct seq_file *p, void *v) | 99 | int show_interrupts(struct seq_file *p, void *v) |
@@ -181,7 +186,7 @@ void fixup_irqs(cpumask_t map) | |||
181 | 186 | ||
182 | void do_IRQ(struct pt_regs *regs) | 187 | void do_IRQ(struct pt_regs *regs) |
183 | { | 188 | { |
184 | int irq; | 189 | unsigned int irq; |
185 | #ifdef CONFIG_IRQSTACKS | 190 | #ifdef CONFIG_IRQSTACKS |
186 | struct thread_info *curtp, *irqtp; | 191 | struct thread_info *curtp, *irqtp; |
187 | #endif | 192 | #endif |
@@ -212,7 +217,7 @@ void do_IRQ(struct pt_regs *regs) | |||
212 | */ | 217 | */ |
213 | irq = ppc_md.get_irq(regs); | 218 | irq = ppc_md.get_irq(regs); |
214 | 219 | ||
215 | if (irq >= 0) { | 220 | if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) { |
216 | #ifdef CONFIG_IRQSTACKS | 221 | #ifdef CONFIG_IRQSTACKS |
217 | /* Switch to the irq stack to handle this */ | 222 | /* Switch to the irq stack to handle this */ |
218 | curtp = current_thread_info(); | 223 | curtp = current_thread_info(); |
@@ -231,7 +236,7 @@ void do_IRQ(struct pt_regs *regs) | |||
231 | } else | 236 | } else |
232 | #endif | 237 | #endif |
233 | generic_handle_irq(irq, regs); | 238 | generic_handle_irq(irq, regs); |
234 | } else if (irq != -2) | 239 | } else if (irq != NO_IRQ_IGNORE) |
235 | /* That's not SMP safe ... but who cares ? */ | 240 | /* That's not SMP safe ... but who cares ? */ |
236 | ppc_spurious_interrupts++; | 241 | ppc_spurious_interrupts++; |
237 | 242 | ||
@@ -254,123 +259,6 @@ void __init init_IRQ(void) | |||
254 | #endif | 259 | #endif |
255 | } | 260 | } |
256 | 261 | ||
257 | #ifdef CONFIG_PPC64 | ||
258 | /* | ||
259 | * Virtual IRQ mapping code, used on systems with XICS interrupt controllers. | ||
260 | */ | ||
261 | |||
262 | #define UNDEFINED_IRQ 0xffffffff | ||
263 | unsigned int virt_irq_to_real_map[NR_IRQS]; | ||
264 | |||
265 | /* | ||
266 | * Don't use virtual irqs 0, 1, 2 for devices. | ||
267 | * The pcnet32 driver considers interrupt numbers < 2 to be invalid, | ||
268 | * and 2 is the XICS IPI interrupt. | ||
269 | * We limit virtual irqs to __irq_offet_value less than virt_irq_max so | ||
270 | * that when we offset them we don't end up with an interrupt | ||
271 | * number >= virt_irq_max. | ||
272 | */ | ||
273 | #define MIN_VIRT_IRQ 3 | ||
274 | |||
275 | unsigned int virt_irq_max; | ||
276 | static unsigned int max_virt_irq; | ||
277 | static unsigned int nr_virt_irqs; | ||
278 | |||
279 | void | ||
280 | virt_irq_init(void) | ||
281 | { | ||
282 | int i; | ||
283 | |||
284 | if ((virt_irq_max == 0) || (virt_irq_max > (NR_IRQS - 1))) | ||
285 | virt_irq_max = NR_IRQS - 1; | ||
286 | max_virt_irq = virt_irq_max - __irq_offset_value; | ||
287 | nr_virt_irqs = max_virt_irq - MIN_VIRT_IRQ + 1; | ||
288 | |||
289 | for (i = 0; i < NR_IRQS; i++) | ||
290 | virt_irq_to_real_map[i] = UNDEFINED_IRQ; | ||
291 | } | ||
292 | |||
293 | /* Create a mapping for a real_irq if it doesn't already exist. | ||
294 | * Return the virtual irq as a convenience. | ||
295 | */ | ||
296 | int virt_irq_create_mapping(unsigned int real_irq) | ||
297 | { | ||
298 | unsigned int virq, first_virq; | ||
299 | static int warned; | ||
300 | |||
301 | if (ppc64_interrupt_controller == IC_OPEN_PIC) | ||
302 | return real_irq; /* no mapping for openpic (for now) */ | ||
303 | |||
304 | if (ppc64_interrupt_controller == IC_CELL_PIC) | ||
305 | return real_irq; /* no mapping for iic either */ | ||
306 | |||
307 | /* don't map interrupts < MIN_VIRT_IRQ */ | ||
308 | if (real_irq < MIN_VIRT_IRQ) { | ||
309 | virt_irq_to_real_map[real_irq] = real_irq; | ||
310 | return real_irq; | ||
311 | } | ||
312 | |||
313 | /* map to a number between MIN_VIRT_IRQ and max_virt_irq */ | ||
314 | virq = real_irq; | ||
315 | if (virq > max_virt_irq) | ||
316 | virq = (virq % nr_virt_irqs) + MIN_VIRT_IRQ; | ||
317 | |||
318 | /* search for this number or a free slot */ | ||
319 | first_virq = virq; | ||
320 | while (virt_irq_to_real_map[virq] != UNDEFINED_IRQ) { | ||
321 | if (virt_irq_to_real_map[virq] == real_irq) | ||
322 | return virq; | ||
323 | if (++virq > max_virt_irq) | ||
324 | virq = MIN_VIRT_IRQ; | ||
325 | if (virq == first_virq) | ||
326 | goto nospace; /* oops, no free slots */ | ||
327 | } | ||
328 | |||
329 | virt_irq_to_real_map[virq] = real_irq; | ||
330 | return virq; | ||
331 | |||
332 | nospace: | ||
333 | if (!warned) { | ||
334 | printk(KERN_CRIT "Interrupt table is full\n"); | ||
335 | printk(KERN_CRIT "Increase virt_irq_max (currently %d) " | ||
336 | "in your kernel sources and rebuild.\n", virt_irq_max); | ||
337 | warned = 1; | ||
338 | } | ||
339 | return NO_IRQ; | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * In most cases will get a hit on the very first slot checked in the | ||
344 | * virt_irq_to_real_map. Only when there are a large number of | ||
345 | * IRQs will this be expensive. | ||
346 | */ | ||
347 | unsigned int real_irq_to_virt_slowpath(unsigned int real_irq) | ||
348 | { | ||
349 | unsigned int virq; | ||
350 | unsigned int first_virq; | ||
351 | |||
352 | virq = real_irq; | ||
353 | |||
354 | if (virq > max_virt_irq) | ||
355 | virq = (virq % nr_virt_irqs) + MIN_VIRT_IRQ; | ||
356 | |||
357 | first_virq = virq; | ||
358 | |||
359 | do { | ||
360 | if (virt_irq_to_real_map[virq] == real_irq) | ||
361 | return virq; | ||
362 | |||
363 | virq++; | ||
364 | |||
365 | if (virq >= max_virt_irq) | ||
366 | virq = 0; | ||
367 | |||
368 | } while (first_virq != virq); | ||
369 | |||
370 | return NO_IRQ; | ||
371 | |||
372 | } | ||
373 | #endif /* CONFIG_PPC64 */ | ||
374 | 262 | ||
375 | #ifdef CONFIG_IRQSTACKS | 263 | #ifdef CONFIG_IRQSTACKS |
376 | struct thread_info *softirq_ctx[NR_CPUS] __read_mostly; | 264 | struct thread_info *softirq_ctx[NR_CPUS] __read_mostly; |
@@ -430,6 +318,503 @@ void do_softirq(void) | |||
430 | } | 318 | } |
431 | EXPORT_SYMBOL(do_softirq); | 319 | EXPORT_SYMBOL(do_softirq); |
432 | 320 | ||
321 | |||
322 | /* | ||
323 | * IRQ controller and virtual interrupts | ||
324 | */ | ||
325 | |||
326 | #ifdef CONFIG_PPC_MERGE | ||
327 | |||
328 | static LIST_HEAD(irq_hosts); | ||
329 | static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED; | ||
330 | |||
331 | struct irq_map_entry irq_map[NR_IRQS]; | ||
332 | static unsigned int irq_virq_count = NR_IRQS; | ||
333 | static struct irq_host *irq_default_host; | ||
334 | |||
335 | struct irq_host *irq_alloc_host(unsigned int revmap_type, | ||
336 | unsigned int revmap_arg, | ||
337 | struct irq_host_ops *ops, | ||
338 | irq_hw_number_t inval_irq) | ||
339 | { | ||
340 | struct irq_host *host; | ||
341 | unsigned int size = sizeof(struct irq_host); | ||
342 | unsigned int i; | ||
343 | unsigned int *rmap; | ||
344 | unsigned long flags; | ||
345 | |||
346 | /* Allocate structure and revmap table if using linear mapping */ | ||
347 | if (revmap_type == IRQ_HOST_MAP_LINEAR) | ||
348 | size += revmap_arg * sizeof(unsigned int); | ||
349 | if (mem_init_done) | ||
350 | host = kzalloc(size, GFP_KERNEL); | ||
351 | else { | ||
352 | host = alloc_bootmem(size); | ||
353 | if (host) | ||
354 | memset(host, 0, size); | ||
355 | } | ||
356 | if (host == NULL) | ||
357 | return NULL; | ||
358 | |||
359 | /* Fill structure */ | ||
360 | host->revmap_type = revmap_type; | ||
361 | host->inval_irq = inval_irq; | ||
362 | host->ops = ops; | ||
363 | |||
364 | spin_lock_irqsave(&irq_big_lock, flags); | ||
365 | |||
366 | /* If it's a legacy controller, check for duplicates and | ||
367 | * mark it as allocated (we use irq 0 host pointer for that | ||
368 | */ | ||
369 | if (revmap_type == IRQ_HOST_MAP_LEGACY) { | ||
370 | if (irq_map[0].host != NULL) { | ||
371 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
372 | /* If we are early boot, we can't free the structure, | ||
373 | * too bad... | ||
374 | * this will be fixed once slab is made available early | ||
375 | * instead of the current cruft | ||
376 | */ | ||
377 | if (mem_init_done) | ||
378 | kfree(host); | ||
379 | return NULL; | ||
380 | } | ||
381 | irq_map[0].host = host; | ||
382 | } | ||
383 | |||
384 | list_add(&host->link, &irq_hosts); | ||
385 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
386 | |||
387 | /* Additional setups per revmap type */ | ||
388 | switch(revmap_type) { | ||
389 | case IRQ_HOST_MAP_LEGACY: | ||
390 | /* 0 is always the invalid number for legacy */ | ||
391 | host->inval_irq = 0; | ||
392 | /* setup us as the host for all legacy interrupts */ | ||
393 | for (i = 1; i < NUM_ISA_INTERRUPTS; i++) { | ||
394 | irq_map[i].hwirq = 0; | ||
395 | smp_wmb(); | ||
396 | irq_map[i].host = host; | ||
397 | smp_wmb(); | ||
398 | |||
399 | /* Clear some flags */ | ||
400 | get_irq_desc(i)->status | ||
401 | &= ~(IRQ_NOREQUEST | IRQ_LEVEL); | ||
402 | |||
403 | /* Legacy flags are left to default at this point, | ||
404 | * one can then use irq_create_mapping() to | ||
405 | * explicitely change them | ||
406 | */ | ||
407 | ops->map(host, i, i, 0); | ||
408 | } | ||
409 | break; | ||
410 | case IRQ_HOST_MAP_LINEAR: | ||
411 | rmap = (unsigned int *)(host + 1); | ||
412 | for (i = 0; i < revmap_arg; i++) | ||
413 | rmap[i] = IRQ_NONE; | ||
414 | host->revmap_data.linear.size = revmap_arg; | ||
415 | smp_wmb(); | ||
416 | host->revmap_data.linear.revmap = rmap; | ||
417 | break; | ||
418 | default: | ||
419 | break; | ||
420 | } | ||
421 | |||
422 | pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type, host); | ||
423 | |||
424 | return host; | ||
425 | } | ||
426 | |||
427 | struct irq_host *irq_find_host(struct device_node *node) | ||
428 | { | ||
429 | struct irq_host *h, *found = NULL; | ||
430 | unsigned long flags; | ||
431 | |||
432 | /* We might want to match the legacy controller last since | ||
433 | * it might potentially be set to match all interrupts in | ||
434 | * the absence of a device node. This isn't a problem so far | ||
435 | * yet though... | ||
436 | */ | ||
437 | spin_lock_irqsave(&irq_big_lock, flags); | ||
438 | list_for_each_entry(h, &irq_hosts, link) | ||
439 | if (h->ops->match == NULL || h->ops->match(h, node)) { | ||
440 | found = h; | ||
441 | break; | ||
442 | } | ||
443 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
444 | return found; | ||
445 | } | ||
446 | EXPORT_SYMBOL_GPL(irq_find_host); | ||
447 | |||
448 | void irq_set_default_host(struct irq_host *host) | ||
449 | { | ||
450 | pr_debug("irq: Default host set to @0x%p\n", host); | ||
451 | |||
452 | irq_default_host = host; | ||
453 | } | ||
454 | |||
455 | void irq_set_virq_count(unsigned int count) | ||
456 | { | ||
457 | pr_debug("irq: Trying to set virq count to %d\n", count); | ||
458 | |||
459 | BUG_ON(count < NUM_ISA_INTERRUPTS); | ||
460 | if (count < NR_IRQS) | ||
461 | irq_virq_count = count; | ||
462 | } | ||
463 | |||
464 | unsigned int irq_create_mapping(struct irq_host *host, | ||
465 | irq_hw_number_t hwirq, | ||
466 | unsigned int flags) | ||
467 | { | ||
468 | unsigned int virq, hint; | ||
469 | |||
470 | pr_debug("irq: irq_create_mapping(0x%p, 0x%lx, 0x%x)\n", | ||
471 | host, hwirq, flags); | ||
472 | |||
473 | /* Look for default host if nececssary */ | ||
474 | if (host == NULL) | ||
475 | host = irq_default_host; | ||
476 | if (host == NULL) { | ||
477 | printk(KERN_WARNING "irq_create_mapping called for" | ||
478 | " NULL host, hwirq=%lx\n", hwirq); | ||
479 | WARN_ON(1); | ||
480 | return NO_IRQ; | ||
481 | } | ||
482 | pr_debug("irq: -> using host @%p\n", host); | ||
483 | |||
484 | /* Check if mapping already exist, if it does, call | ||
485 | * host->ops->map() to update the flags | ||
486 | */ | ||
487 | virq = irq_find_mapping(host, hwirq); | ||
488 | if (virq != IRQ_NONE) { | ||
489 | pr_debug("irq: -> existing mapping on virq %d\n", virq); | ||
490 | host->ops->map(host, virq, hwirq, flags); | ||
491 | return virq; | ||
492 | } | ||
493 | |||
494 | /* Get a virtual interrupt number */ | ||
495 | if (host->revmap_type == IRQ_HOST_MAP_LEGACY) { | ||
496 | /* Handle legacy */ | ||
497 | virq = (unsigned int)hwirq; | ||
498 | if (virq == 0 || virq >= NUM_ISA_INTERRUPTS) | ||
499 | return NO_IRQ; | ||
500 | return virq; | ||
501 | } else { | ||
502 | /* Allocate a virtual interrupt number */ | ||
503 | hint = hwirq % irq_virq_count; | ||
504 | virq = irq_alloc_virt(host, 1, hint); | ||
505 | if (virq == NO_IRQ) { | ||
506 | pr_debug("irq: -> virq allocation failed\n"); | ||
507 | return NO_IRQ; | ||
508 | } | ||
509 | } | ||
510 | pr_debug("irq: -> obtained virq %d\n", virq); | ||
511 | |||
512 | /* Clear some flags */ | ||
513 | get_irq_desc(virq)->status &= ~(IRQ_NOREQUEST | IRQ_LEVEL); | ||
514 | |||
515 | /* map it */ | ||
516 | if (host->ops->map(host, virq, hwirq, flags)) { | ||
517 | pr_debug("irq: -> mapping failed, freeing\n"); | ||
518 | irq_free_virt(virq, 1); | ||
519 | return NO_IRQ; | ||
520 | } | ||
521 | smp_wmb(); | ||
522 | irq_map[virq].hwirq = hwirq; | ||
523 | smp_mb(); | ||
524 | return virq; | ||
525 | } | ||
526 | EXPORT_SYMBOL_GPL(irq_create_mapping); | ||
527 | |||
528 | extern unsigned int irq_create_of_mapping(struct device_node *controller, | ||
529 | u32 *intspec, unsigned int intsize) | ||
530 | { | ||
531 | struct irq_host *host; | ||
532 | irq_hw_number_t hwirq; | ||
533 | unsigned int flags = IRQ_TYPE_NONE; | ||
534 | |||
535 | if (controller == NULL) | ||
536 | host = irq_default_host; | ||
537 | else | ||
538 | host = irq_find_host(controller); | ||
539 | if (host == NULL) | ||
540 | return NO_IRQ; | ||
541 | |||
542 | /* If host has no translation, then we assume interrupt line */ | ||
543 | if (host->ops->xlate == NULL) | ||
544 | hwirq = intspec[0]; | ||
545 | else { | ||
546 | if (host->ops->xlate(host, controller, intspec, intsize, | ||
547 | &hwirq, &flags)) | ||
548 | return NO_IRQ; | ||
549 | } | ||
550 | |||
551 | return irq_create_mapping(host, hwirq, flags); | ||
552 | } | ||
553 | EXPORT_SYMBOL_GPL(irq_create_of_mapping); | ||
554 | |||
555 | unsigned int irq_of_parse_and_map(struct device_node *dev, int index) | ||
556 | { | ||
557 | struct of_irq oirq; | ||
558 | |||
559 | if (of_irq_map_one(dev, index, &oirq)) | ||
560 | return NO_IRQ; | ||
561 | |||
562 | return irq_create_of_mapping(oirq.controller, oirq.specifier, | ||
563 | oirq.size); | ||
564 | } | ||
565 | EXPORT_SYMBOL_GPL(irq_of_parse_and_map); | ||
566 | |||
567 | void irq_dispose_mapping(unsigned int virq) | ||
568 | { | ||
569 | struct irq_host *host = irq_map[virq].host; | ||
570 | irq_hw_number_t hwirq; | ||
571 | unsigned long flags; | ||
572 | |||
573 | WARN_ON (host == NULL); | ||
574 | if (host == NULL) | ||
575 | return; | ||
576 | |||
577 | /* Never unmap legacy interrupts */ | ||
578 | if (host->revmap_type == IRQ_HOST_MAP_LEGACY) | ||
579 | return; | ||
580 | |||
581 | /* remove chip and handler */ | ||
582 | set_irq_chip_and_handler(virq, NULL, NULL); | ||
583 | |||
584 | /* Make sure it's completed */ | ||
585 | synchronize_irq(virq); | ||
586 | |||
587 | /* Tell the PIC about it */ | ||
588 | if (host->ops->unmap) | ||
589 | host->ops->unmap(host, virq); | ||
590 | smp_mb(); | ||
591 | |||
592 | /* Clear reverse map */ | ||
593 | hwirq = irq_map[virq].hwirq; | ||
594 | switch(host->revmap_type) { | ||
595 | case IRQ_HOST_MAP_LINEAR: | ||
596 | if (hwirq < host->revmap_data.linear.size) | ||
597 | host->revmap_data.linear.revmap[hwirq] = IRQ_NONE; | ||
598 | break; | ||
599 | case IRQ_HOST_MAP_TREE: | ||
600 | /* Check if radix tree allocated yet */ | ||
601 | if (host->revmap_data.tree.gfp_mask == 0) | ||
602 | break; | ||
603 | /* XXX radix tree not safe ! remove lock whem it becomes safe | ||
604 | * and use some RCU sync to make sure everything is ok before we | ||
605 | * can re-use that map entry | ||
606 | */ | ||
607 | spin_lock_irqsave(&irq_big_lock, flags); | ||
608 | radix_tree_delete(&host->revmap_data.tree, hwirq); | ||
609 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
610 | break; | ||
611 | } | ||
612 | |||
613 | /* Destroy map */ | ||
614 | smp_mb(); | ||
615 | irq_map[virq].hwirq = host->inval_irq; | ||
616 | |||
617 | /* Set some flags */ | ||
618 | get_irq_desc(virq)->status |= IRQ_NOREQUEST; | ||
619 | |||
620 | /* Free it */ | ||
621 | irq_free_virt(virq, 1); | ||
622 | } | ||
623 | EXPORT_SYMBOL_GPL(irq_dispose_mapping); | ||
624 | |||
625 | unsigned int irq_find_mapping(struct irq_host *host, | ||
626 | irq_hw_number_t hwirq) | ||
627 | { | ||
628 | unsigned int i; | ||
629 | unsigned int hint = hwirq % irq_virq_count; | ||
630 | |||
631 | /* Look for default host if nececssary */ | ||
632 | if (host == NULL) | ||
633 | host = irq_default_host; | ||
634 | if (host == NULL) | ||
635 | return NO_IRQ; | ||
636 | |||
637 | /* legacy -> bail early */ | ||
638 | if (host->revmap_type == IRQ_HOST_MAP_LEGACY) | ||
639 | return hwirq; | ||
640 | |||
641 | /* Slow path does a linear search of the map */ | ||
642 | if (hint < NUM_ISA_INTERRUPTS) | ||
643 | hint = NUM_ISA_INTERRUPTS; | ||
644 | i = hint; | ||
645 | do { | ||
646 | if (irq_map[i].host == host && | ||
647 | irq_map[i].hwirq == hwirq) | ||
648 | return i; | ||
649 | i++; | ||
650 | if (i >= irq_virq_count) | ||
651 | i = NUM_ISA_INTERRUPTS; | ||
652 | } while(i != hint); | ||
653 | return NO_IRQ; | ||
654 | } | ||
655 | EXPORT_SYMBOL_GPL(irq_find_mapping); | ||
656 | |||
657 | |||
658 | unsigned int irq_radix_revmap(struct irq_host *host, | ||
659 | irq_hw_number_t hwirq) | ||
660 | { | ||
661 | struct radix_tree_root *tree; | ||
662 | struct irq_map_entry *ptr; | ||
663 | unsigned int virq; | ||
664 | unsigned long flags; | ||
665 | |||
666 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); | ||
667 | |||
668 | /* Check if the radix tree exist yet. We test the value of | ||
669 | * the gfp_mask for that. Sneaky but saves another int in the | ||
670 | * structure. If not, we fallback to slow mode | ||
671 | */ | ||
672 | tree = &host->revmap_data.tree; | ||
673 | if (tree->gfp_mask == 0) | ||
674 | return irq_find_mapping(host, hwirq); | ||
675 | |||
676 | /* XXX Current radix trees are NOT SMP safe !!! Remove that lock | ||
677 | * when that is fixed (when Nick's patch gets in | ||
678 | */ | ||
679 | spin_lock_irqsave(&irq_big_lock, flags); | ||
680 | |||
681 | /* Now try to resolve */ | ||
682 | ptr = radix_tree_lookup(tree, hwirq); | ||
683 | /* Found it, return */ | ||
684 | if (ptr) { | ||
685 | virq = ptr - irq_map; | ||
686 | goto bail; | ||
687 | } | ||
688 | |||
689 | /* If not there, try to insert it */ | ||
690 | virq = irq_find_mapping(host, hwirq); | ||
691 | if (virq != NO_IRQ) | ||
692 | radix_tree_insert(tree, virq, &irq_map[virq]); | ||
693 | bail: | ||
694 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
695 | return virq; | ||
696 | } | ||
697 | |||
698 | unsigned int irq_linear_revmap(struct irq_host *host, | ||
699 | irq_hw_number_t hwirq) | ||
700 | { | ||
701 | unsigned int *revmap; | ||
702 | |||
703 | WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR); | ||
704 | |||
705 | /* Check revmap bounds */ | ||
706 | if (unlikely(hwirq >= host->revmap_data.linear.size)) | ||
707 | return irq_find_mapping(host, hwirq); | ||
708 | |||
709 | /* Check if revmap was allocated */ | ||
710 | revmap = host->revmap_data.linear.revmap; | ||
711 | if (unlikely(revmap == NULL)) | ||
712 | return irq_find_mapping(host, hwirq); | ||
713 | |||
714 | /* Fill up revmap with slow path if no mapping found */ | ||
715 | if (unlikely(revmap[hwirq] == NO_IRQ)) | ||
716 | revmap[hwirq] = irq_find_mapping(host, hwirq); | ||
717 | |||
718 | return revmap[hwirq]; | ||
719 | } | ||
720 | |||
721 | unsigned int irq_alloc_virt(struct irq_host *host, | ||
722 | unsigned int count, | ||
723 | unsigned int hint) | ||
724 | { | ||
725 | unsigned long flags; | ||
726 | unsigned int i, j, found = NO_IRQ; | ||
727 | unsigned int limit = irq_virq_count - count; | ||
728 | |||
729 | if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS)) | ||
730 | return NO_IRQ; | ||
731 | |||
732 | spin_lock_irqsave(&irq_big_lock, flags); | ||
733 | |||
734 | /* Use hint for 1 interrupt if any */ | ||
735 | if (count == 1 && hint >= NUM_ISA_INTERRUPTS && | ||
736 | hint < irq_virq_count && irq_map[hint].host == NULL) { | ||
737 | found = hint; | ||
738 | goto hint_found; | ||
739 | } | ||
740 | |||
741 | /* Look for count consecutive numbers in the allocatable | ||
742 | * (non-legacy) space | ||
743 | */ | ||
744 | for (i = NUM_ISA_INTERRUPTS; i <= limit; ) { | ||
745 | for (j = i; j < (i + count); j++) | ||
746 | if (irq_map[j].host != NULL) { | ||
747 | i = j + 1; | ||
748 | continue; | ||
749 | } | ||
750 | found = i; | ||
751 | break; | ||
752 | } | ||
753 | if (found == NO_IRQ) { | ||
754 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
755 | return NO_IRQ; | ||
756 | } | ||
757 | hint_found: | ||
758 | for (i = found; i < (found + count); i++) { | ||
759 | irq_map[i].hwirq = host->inval_irq; | ||
760 | smp_wmb(); | ||
761 | irq_map[i].host = host; | ||
762 | } | ||
763 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
764 | return found; | ||
765 | } | ||
766 | |||
767 | void irq_free_virt(unsigned int virq, unsigned int count) | ||
768 | { | ||
769 | unsigned long flags; | ||
770 | unsigned int i; | ||
771 | |||
772 | WARN_ON (virq < NUM_ISA_INTERRUPTS); | ||
773 | WARN_ON (count == 0 || (virq + count) > irq_virq_count); | ||
774 | |||
775 | spin_lock_irqsave(&irq_big_lock, flags); | ||
776 | for (i = virq; i < (virq + count); i++) { | ||
777 | struct irq_host *host; | ||
778 | |||
779 | if (i < NUM_ISA_INTERRUPTS || | ||
780 | (virq + count) > irq_virq_count) | ||
781 | continue; | ||
782 | |||
783 | host = irq_map[i].host; | ||
784 | irq_map[i].hwirq = host->inval_irq; | ||
785 | smp_wmb(); | ||
786 | irq_map[i].host = NULL; | ||
787 | } | ||
788 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
789 | } | ||
790 | |||
791 | void irq_early_init(void) | ||
792 | { | ||
793 | unsigned int i; | ||
794 | |||
795 | for (i = 0; i < NR_IRQS; i++) | ||
796 | get_irq_desc(i)->status |= IRQ_NOREQUEST; | ||
797 | } | ||
798 | |||
799 | /* We need to create the radix trees late */ | ||
800 | static int irq_late_init(void) | ||
801 | { | ||
802 | struct irq_host *h; | ||
803 | unsigned long flags; | ||
804 | |||
805 | spin_lock_irqsave(&irq_big_lock, flags); | ||
806 | list_for_each_entry(h, &irq_hosts, link) { | ||
807 | if (h->revmap_type == IRQ_HOST_MAP_TREE) | ||
808 | INIT_RADIX_TREE(&h->revmap_data.tree, GFP_ATOMIC); | ||
809 | } | ||
810 | spin_unlock_irqrestore(&irq_big_lock, flags); | ||
811 | |||
812 | return 0; | ||
813 | } | ||
814 | arch_initcall(irq_late_init); | ||
815 | |||
816 | #endif /* CONFIG_PPC_MERGE */ | ||
817 | |||
433 | #ifdef CONFIG_PCI_MSI | 818 | #ifdef CONFIG_PCI_MSI |
434 | int pci_enable_msi(struct pci_dev * pdev) | 819 | int pci_enable_msi(struct pci_dev * pdev) |
435 | { | 820 | { |