diff options
Diffstat (limited to 'arch/sparc64/kernel/irq.c')
-rw-r--r-- | arch/sparc64/kernel/irq.c | 104 |
1 files changed, 95 insertions, 9 deletions
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 2873835e2626..b5ff3ee5ace1 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/seq_file.h> | 22 | #include <linux/seq_file.h> |
23 | #include <linux/bootmem.h> | 23 | #include <linux/bootmem.h> |
24 | #include <linux/irq.h> | 24 | #include <linux/irq.h> |
25 | #include <linux/msi.h> | ||
25 | 26 | ||
26 | #include <asm/ptrace.h> | 27 | #include <asm/ptrace.h> |
27 | #include <asm/processor.h> | 28 | #include <asm/processor.h> |
@@ -87,7 +88,6 @@ struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (SMP_CACHE_BY | |||
87 | #define irq_work(__cpu) &(trap_block[(__cpu)].irq_worklist) | 88 | #define irq_work(__cpu) &(trap_block[(__cpu)].irq_worklist) |
88 | 89 | ||
89 | static unsigned int virt_to_real_irq_table[NR_IRQS]; | 90 | static unsigned int virt_to_real_irq_table[NR_IRQS]; |
90 | static unsigned char virt_irq_cur = 1; | ||
91 | 91 | ||
92 | static unsigned char virt_irq_alloc(unsigned int real_irq) | 92 | static unsigned char virt_irq_alloc(unsigned int real_irq) |
93 | { | 93 | { |
@@ -95,26 +95,32 @@ static unsigned char virt_irq_alloc(unsigned int real_irq) | |||
95 | 95 | ||
96 | BUILD_BUG_ON(NR_IRQS >= 256); | 96 | BUILD_BUG_ON(NR_IRQS >= 256); |
97 | 97 | ||
98 | ent = virt_irq_cur; | 98 | for (ent = 1; ent < NR_IRQS; ent++) { |
99 | if (!virt_to_real_irq_table[ent]) | ||
100 | break; | ||
101 | } | ||
99 | if (ent >= NR_IRQS) { | 102 | if (ent >= NR_IRQS) { |
100 | printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); | 103 | printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); |
101 | return 0; | 104 | return 0; |
102 | } | 105 | } |
103 | 106 | ||
104 | virt_irq_cur = ent + 1; | ||
105 | virt_to_real_irq_table[ent] = real_irq; | 107 | virt_to_real_irq_table[ent] = real_irq; |
106 | 108 | ||
107 | return ent; | 109 | return ent; |
108 | } | 110 | } |
109 | 111 | ||
110 | #if 0 /* Currently unused. */ | 112 | static void virt_irq_free(unsigned int virt_irq) |
111 | static unsigned char real_to_virt_irq(unsigned int real_irq) | ||
112 | { | 113 | { |
113 | struct ino_bucket *bucket = __bucket(real_irq); | 114 | unsigned int real_irq; |
114 | 115 | ||
115 | return bucket->virt_irq; | 116 | if (virt_irq >= NR_IRQS) |
117 | return; | ||
118 | |||
119 | real_irq = virt_to_real_irq_table[virt_irq]; | ||
120 | virt_to_real_irq_table[virt_irq] = 0; | ||
121 | |||
122 | __bucket(real_irq)->virt_irq = 0; | ||
116 | } | 123 | } |
117 | #endif | ||
118 | 124 | ||
119 | static unsigned int virt_to_real_irq(unsigned char virt_irq) | 125 | static unsigned int virt_to_real_irq(unsigned char virt_irq) |
120 | { | 126 | { |
@@ -341,6 +347,20 @@ static void sun4v_irq_disable(unsigned int virt_irq) | |||
341 | } | 347 | } |
342 | } | 348 | } |
343 | 349 | ||
350 | #ifdef CONFIG_PCI_MSI | ||
351 | static void sun4v_msi_enable(unsigned int virt_irq) | ||
352 | { | ||
353 | sun4v_irq_enable(virt_irq); | ||
354 | unmask_msi_irq(virt_irq); | ||
355 | } | ||
356 | |||
357 | static void sun4v_msi_disable(unsigned int virt_irq) | ||
358 | { | ||
359 | mask_msi_irq(virt_irq); | ||
360 | sun4v_irq_disable(virt_irq); | ||
361 | } | ||
362 | #endif | ||
363 | |||
344 | static void sun4v_irq_end(unsigned int virt_irq) | 364 | static void sun4v_irq_end(unsigned int virt_irq) |
345 | { | 365 | { |
346 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 366 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
@@ -398,6 +418,18 @@ static struct irq_chip sun4v_irq_ack = { | |||
398 | .end = sun4v_irq_end, | 418 | .end = sun4v_irq_end, |
399 | }; | 419 | }; |
400 | 420 | ||
421 | #ifdef CONFIG_PCI_MSI | ||
422 | static struct irq_chip sun4v_msi = { | ||
423 | .typename = "sun4v+msi", | ||
424 | .mask = mask_msi_irq, | ||
425 | .unmask = unmask_msi_irq, | ||
426 | .enable = sun4v_msi_enable, | ||
427 | .disable = sun4v_msi_disable, | ||
428 | .ack = run_pre_handler, | ||
429 | .end = sun4v_irq_end, | ||
430 | }; | ||
431 | #endif | ||
432 | |||
401 | void irq_install_pre_handler(int virt_irq, | 433 | void irq_install_pre_handler(int virt_irq, |
402 | void (*func)(unsigned int, void *, void *), | 434 | void (*func)(unsigned int, void *, void *), |
403 | void *arg1, void *arg2) | 435 | void *arg1, void *arg2) |
@@ -411,7 +443,11 @@ void irq_install_pre_handler(int virt_irq, | |||
411 | 443 | ||
412 | chip = get_irq_chip(virt_irq); | 444 | chip = get_irq_chip(virt_irq); |
413 | if (chip == &sun4u_irq_ack || | 445 | if (chip == &sun4u_irq_ack || |
414 | chip == &sun4v_irq_ack) | 446 | chip == &sun4v_irq_ack |
447 | #ifdef CONFIG_PCI_MSI | ||
448 | || chip == &sun4v_msi | ||
449 | #endif | ||
450 | ) | ||
415 | return; | 451 | return; |
416 | 452 | ||
417 | chip = (chip == &sun4u_irq ? | 453 | chip = (chip == &sun4u_irq ? |
@@ -489,6 +525,56 @@ out: | |||
489 | return bucket->virt_irq; | 525 | return bucket->virt_irq; |
490 | } | 526 | } |
491 | 527 | ||
528 | #ifdef CONFIG_PCI_MSI | ||
529 | unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p, | ||
530 | unsigned int msi_start, unsigned int msi_end) | ||
531 | { | ||
532 | struct ino_bucket *bucket; | ||
533 | struct irq_handler_data *data; | ||
534 | unsigned long sysino; | ||
535 | unsigned int devino; | ||
536 | |||
537 | BUG_ON(tlb_type != hypervisor); | ||
538 | |||
539 | /* Find a free devino in the given range. */ | ||
540 | for (devino = msi_start; devino < msi_end; devino++) { | ||
541 | sysino = sun4v_devino_to_sysino(devhandle, devino); | ||
542 | bucket = &ivector_table[sysino]; | ||
543 | if (!bucket->virt_irq) | ||
544 | break; | ||
545 | } | ||
546 | if (devino >= msi_end) | ||
547 | return 0; | ||
548 | |||
549 | sysino = sun4v_devino_to_sysino(devhandle, devino); | ||
550 | bucket = &ivector_table[sysino]; | ||
551 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | ||
552 | *virt_irq_p = bucket->virt_irq; | ||
553 | set_irq_chip(bucket->virt_irq, &sun4v_msi); | ||
554 | |||
555 | data = get_irq_chip_data(bucket->virt_irq); | ||
556 | if (unlikely(data)) | ||
557 | return devino; | ||
558 | |||
559 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | ||
560 | if (unlikely(!data)) { | ||
561 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); | ||
562 | prom_halt(); | ||
563 | } | ||
564 | set_irq_chip_data(bucket->virt_irq, data); | ||
565 | |||
566 | data->imap = ~0UL; | ||
567 | data->iclr = ~0UL; | ||
568 | |||
569 | return devino; | ||
570 | } | ||
571 | |||
572 | void sun4v_destroy_msi(unsigned int virt_irq) | ||
573 | { | ||
574 | virt_irq_free(virt_irq); | ||
575 | } | ||
576 | #endif | ||
577 | |||
492 | void ack_bad_irq(unsigned int virt_irq) | 578 | void ack_bad_irq(unsigned int virt_irq) |
493 | { | 579 | { |
494 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 580 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |