diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-10-11 06:16:13 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-14 00:53:13 -0400 |
commit | 759f89e03c9e5656ff18c02e21b439506f7c0cdc (patch) | |
tree | 6e7703c0672210f2c0a1168de161d15c7470081d /arch/sparc64/kernel/irq.c | |
parent | a2cd15586e630b0870bf34783568d83901890743 (diff) |
[SPARC64]: Consolidate MSI support code.
This also makes us use the MSI queues correctly.
Each MSI queue is serviced by a normal sun4u/sun4v INO interrupt
handler. This handler runs the MSI queue and dispatches the
virtual interrupts indicated by arriving MSIs in that MSI queue.
All of the common logic is placed in pci_msi.c, with callbacks to
handle the PCI controller specific aspects of the operations.
This common infrastructure will make it much easier to add MSG
support.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/irq.c')
-rw-r--r-- | arch/sparc64/kernel/irq.c | 230 |
1 files changed, 23 insertions, 207 deletions
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 7f5a4c77e3e4..045ab27d4271 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c | |||
@@ -21,7 +21,6 @@ | |||
21 | #include <linux/seq_file.h> | 21 | #include <linux/seq_file.h> |
22 | #include <linux/bootmem.h> | 22 | #include <linux/bootmem.h> |
23 | #include <linux/irq.h> | 23 | #include <linux/irq.h> |
24 | #include <linux/msi.h> | ||
25 | 24 | ||
26 | #include <asm/ptrace.h> | 25 | #include <asm/ptrace.h> |
27 | #include <asm/processor.h> | 26 | #include <asm/processor.h> |
@@ -92,39 +91,46 @@ static struct { | |||
92 | unsigned int dev_handle; | 91 | unsigned int dev_handle; |
93 | unsigned int dev_ino; | 92 | unsigned int dev_ino; |
94 | } virt_to_real_irq_table[NR_IRQS]; | 93 | } virt_to_real_irq_table[NR_IRQS]; |
94 | static DEFINE_SPINLOCK(virt_irq_alloc_lock); | ||
95 | 95 | ||
96 | static unsigned char virt_irq_alloc(unsigned int real_irq) | 96 | unsigned char virt_irq_alloc(unsigned int real_irq) |
97 | { | 97 | { |
98 | unsigned long flags; | ||
98 | unsigned char ent; | 99 | unsigned char ent; |
99 | 100 | ||
100 | BUILD_BUG_ON(NR_IRQS >= 256); | 101 | BUILD_BUG_ON(NR_IRQS >= 256); |
101 | 102 | ||
103 | spin_lock_irqsave(&virt_irq_alloc_lock, flags); | ||
104 | |||
102 | for (ent = 1; ent < NR_IRQS; ent++) { | 105 | for (ent = 1; ent < NR_IRQS; ent++) { |
103 | if (!virt_to_real_irq_table[ent].irq) | 106 | if (!virt_to_real_irq_table[ent].irq) |
104 | break; | 107 | break; |
105 | } | 108 | } |
106 | if (ent >= NR_IRQS) { | 109 | if (ent >= NR_IRQS) { |
107 | printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); | 110 | printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); |
108 | return 0; | 111 | ent = 0; |
112 | } else { | ||
113 | virt_to_real_irq_table[ent].irq = real_irq; | ||
109 | } | 114 | } |
110 | 115 | ||
111 | virt_to_real_irq_table[ent].irq = real_irq; | 116 | spin_unlock_irqrestore(&virt_irq_alloc_lock, flags); |
112 | 117 | ||
113 | return ent; | 118 | return ent; |
114 | } | 119 | } |
115 | 120 | ||
116 | #ifdef CONFIG_PCI_MSI | 121 | #ifdef CONFIG_PCI_MSI |
117 | static void virt_irq_free(unsigned int virt_irq) | 122 | void virt_irq_free(unsigned int virt_irq) |
118 | { | 123 | { |
119 | unsigned int real_irq; | 124 | unsigned long flags; |
120 | 125 | ||
121 | if (virt_irq >= NR_IRQS) | 126 | if (virt_irq >= NR_IRQS) |
122 | return; | 127 | return; |
123 | 128 | ||
124 | real_irq = virt_to_real_irq_table[virt_irq].irq; | 129 | spin_lock_irqsave(&virt_irq_alloc_lock, flags); |
130 | |||
125 | virt_to_real_irq_table[virt_irq].irq = 0; | 131 | virt_to_real_irq_table[virt_irq].irq = 0; |
126 | 132 | ||
127 | __bucket(real_irq)->virt_irq = 0; | 133 | spin_unlock_irqrestore(&virt_irq_alloc_lock, flags); |
128 | } | 134 | } |
129 | #endif | 135 | #endif |
130 | 136 | ||
@@ -217,27 +223,8 @@ struct irq_handler_data { | |||
217 | void (*pre_handler)(unsigned int, void *, void *); | 223 | void (*pre_handler)(unsigned int, void *, void *); |
218 | void *pre_handler_arg1; | 224 | void *pre_handler_arg1; |
219 | void *pre_handler_arg2; | 225 | void *pre_handler_arg2; |
220 | |||
221 | u32 msi; | ||
222 | }; | 226 | }; |
223 | 227 | ||
224 | void sparc64_set_msi(unsigned int virt_irq, u32 msi) | ||
225 | { | ||
226 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); | ||
227 | |||
228 | if (data) | ||
229 | data->msi = msi; | ||
230 | } | ||
231 | |||
232 | u32 sparc64_get_msi(unsigned int virt_irq) | ||
233 | { | ||
234 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); | ||
235 | |||
236 | if (data) | ||
237 | return data->msi; | ||
238 | return 0xffffffff; | ||
239 | } | ||
240 | |||
241 | static inline struct ino_bucket *virt_irq_to_bucket(unsigned int virt_irq) | 228 | static inline struct ino_bucket *virt_irq_to_bucket(unsigned int virt_irq) |
242 | { | 229 | { |
243 | unsigned int real_irq = virt_to_real_irq(virt_irq); | 230 | unsigned int real_irq = virt_to_real_irq(virt_irq); |
@@ -405,32 +392,6 @@ static void sun4v_irq_disable(unsigned int virt_irq) | |||
405 | } | 392 | } |
406 | } | 393 | } |
407 | 394 | ||
408 | #ifdef CONFIG_PCI_MSI | ||
409 | static void sun4u_msi_enable(unsigned int virt_irq) | ||
410 | { | ||
411 | sun4u_irq_enable(virt_irq); | ||
412 | unmask_msi_irq(virt_irq); | ||
413 | } | ||
414 | |||
415 | static void sun4u_msi_disable(unsigned int virt_irq) | ||
416 | { | ||
417 | mask_msi_irq(virt_irq); | ||
418 | sun4u_irq_disable(virt_irq); | ||
419 | } | ||
420 | |||
421 | static void sun4v_msi_enable(unsigned int virt_irq) | ||
422 | { | ||
423 | sun4v_irq_enable(virt_irq); | ||
424 | unmask_msi_irq(virt_irq); | ||
425 | } | ||
426 | |||
427 | static void sun4v_msi_disable(unsigned int virt_irq) | ||
428 | { | ||
429 | mask_msi_irq(virt_irq); | ||
430 | sun4v_irq_disable(virt_irq); | ||
431 | } | ||
432 | #endif | ||
433 | |||
434 | static void sun4v_irq_end(unsigned int virt_irq) | 395 | static void sun4v_irq_end(unsigned int virt_irq) |
435 | { | 396 | { |
436 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 397 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
@@ -585,39 +546,6 @@ static struct irq_chip sun4v_irq = { | |||
585 | .set_affinity = sun4v_set_affinity, | 546 | .set_affinity = sun4v_set_affinity, |
586 | }; | 547 | }; |
587 | 548 | ||
588 | static struct irq_chip sun4v_irq_ack = { | ||
589 | .typename = "sun4v+ack", | ||
590 | .enable = sun4v_irq_enable, | ||
591 | .disable = sun4v_irq_disable, | ||
592 | .ack = run_pre_handler, | ||
593 | .end = sun4v_irq_end, | ||
594 | .set_affinity = sun4v_set_affinity, | ||
595 | }; | ||
596 | |||
597 | #ifdef CONFIG_PCI_MSI | ||
598 | static struct irq_chip sun4u_msi = { | ||
599 | .typename = "sun4u+msi", | ||
600 | .mask = mask_msi_irq, | ||
601 | .unmask = unmask_msi_irq, | ||
602 | .enable = sun4u_msi_enable, | ||
603 | .disable = sun4u_msi_disable, | ||
604 | .ack = run_pre_handler, | ||
605 | .end = sun4u_irq_end, | ||
606 | .set_affinity = sun4u_set_affinity, | ||
607 | }; | ||
608 | |||
609 | static struct irq_chip sun4v_msi = { | ||
610 | .typename = "sun4v+msi", | ||
611 | .mask = mask_msi_irq, | ||
612 | .unmask = unmask_msi_irq, | ||
613 | .enable = sun4v_msi_enable, | ||
614 | .disable = sun4v_msi_disable, | ||
615 | .ack = run_pre_handler, | ||
616 | .end = sun4v_irq_end, | ||
617 | .set_affinity = sun4v_set_affinity, | ||
618 | }; | ||
619 | #endif | ||
620 | |||
621 | static struct irq_chip sun4v_virq = { | 549 | static struct irq_chip sun4v_virq = { |
622 | .typename = "vsun4v", | 550 | .typename = "vsun4v", |
623 | .enable = sun4v_virq_enable, | 551 | .enable = sun4v_virq_enable, |
@@ -626,42 +554,27 @@ static struct irq_chip sun4v_virq = { | |||
626 | .set_affinity = sun4v_virt_set_affinity, | 554 | .set_affinity = sun4v_virt_set_affinity, |
627 | }; | 555 | }; |
628 | 556 | ||
629 | static struct irq_chip sun4v_virq_ack = { | ||
630 | .typename = "vsun4v+ack", | ||
631 | .enable = sun4v_virq_enable, | ||
632 | .disable = sun4v_virq_disable, | ||
633 | .ack = run_pre_handler, | ||
634 | .end = sun4v_virq_end, | ||
635 | .set_affinity = sun4v_virt_set_affinity, | ||
636 | }; | ||
637 | |||
638 | void irq_install_pre_handler(int virt_irq, | 557 | void irq_install_pre_handler(int virt_irq, |
639 | void (*func)(unsigned int, void *, void *), | 558 | void (*func)(unsigned int, void *, void *), |
640 | void *arg1, void *arg2) | 559 | void *arg1, void *arg2) |
641 | { | 560 | { |
642 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); | 561 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
643 | struct irq_chip *chip; | 562 | struct irq_chip *chip = get_irq_chip(virt_irq); |
563 | |||
564 | if (WARN_ON(chip == &sun4v_irq || chip == &sun4v_virq)) { | ||
565 | printk(KERN_ERR "IRQ: Trying to install pre-handler on " | ||
566 | "sun4v irq %u\n", virt_irq); | ||
567 | return; | ||
568 | } | ||
644 | 569 | ||
645 | data->pre_handler = func; | 570 | data->pre_handler = func; |
646 | data->pre_handler_arg1 = arg1; | 571 | data->pre_handler_arg1 = arg1; |
647 | data->pre_handler_arg2 = arg2; | 572 | data->pre_handler_arg2 = arg2; |
648 | 573 | ||
649 | chip = get_irq_chip(virt_irq); | 574 | if (chip == &sun4u_irq_ack) |
650 | if (chip == &sun4u_irq_ack || | ||
651 | chip == &sun4v_irq_ack || | ||
652 | chip == &sun4v_virq_ack | ||
653 | #ifdef CONFIG_PCI_MSI | ||
654 | || chip == &sun4u_msi | ||
655 | || chip == &sun4v_msi | ||
656 | #endif | ||
657 | ) | ||
658 | return; | 575 | return; |
659 | 576 | ||
660 | chip = (chip == &sun4u_irq ? | 577 | set_irq_chip(virt_irq, &sun4u_irq_ack); |
661 | &sun4u_irq_ack : | ||
662 | (chip == &sun4v_irq ? | ||
663 | &sun4v_irq_ack : &sun4v_virq_ack)); | ||
664 | set_irq_chip(virt_irq, chip); | ||
665 | } | 578 | } |
666 | 579 | ||
667 | unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) | 580 | unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) |
@@ -765,103 +678,6 @@ unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino) | |||
765 | return virq; | 678 | return virq; |
766 | } | 679 | } |
767 | 680 | ||
768 | #ifdef CONFIG_PCI_MSI | ||
769 | unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p, | ||
770 | unsigned int msi_start, unsigned int msi_end) | ||
771 | { | ||
772 | struct ino_bucket *bucket; | ||
773 | struct irq_handler_data *data; | ||
774 | unsigned long sysino; | ||
775 | unsigned int devino; | ||
776 | |||
777 | BUG_ON(tlb_type != hypervisor); | ||
778 | |||
779 | /* Find a free devino in the given range. */ | ||
780 | for (devino = msi_start; devino < msi_end; devino++) { | ||
781 | sysino = sun4v_devino_to_sysino(devhandle, devino); | ||
782 | bucket = &ivector_table[sysino]; | ||
783 | if (!bucket->virt_irq) | ||
784 | break; | ||
785 | } | ||
786 | if (devino >= msi_end) | ||
787 | return -ENOSPC; | ||
788 | |||
789 | sysino = sun4v_devino_to_sysino(devhandle, devino); | ||
790 | bucket = &ivector_table[sysino]; | ||
791 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | ||
792 | *virt_irq_p = bucket->virt_irq; | ||
793 | set_irq_chip(bucket->virt_irq, &sun4v_msi); | ||
794 | |||
795 | data = get_irq_chip_data(bucket->virt_irq); | ||
796 | if (unlikely(data)) | ||
797 | return devino; | ||
798 | |||
799 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | ||
800 | if (unlikely(!data)) { | ||
801 | virt_irq_free(*virt_irq_p); | ||
802 | return -ENOMEM; | ||
803 | } | ||
804 | set_irq_chip_data(bucket->virt_irq, data); | ||
805 | |||
806 | data->imap = ~0UL; | ||
807 | data->iclr = ~0UL; | ||
808 | |||
809 | return devino; | ||
810 | } | ||
811 | |||
812 | void sun4v_destroy_msi(unsigned int virt_irq) | ||
813 | { | ||
814 | virt_irq_free(virt_irq); | ||
815 | } | ||
816 | |||
817 | unsigned int sun4u_build_msi(u32 portid, unsigned int *virt_irq_p, | ||
818 | unsigned int msi_start, unsigned int msi_end, | ||
819 | unsigned long imap_base, unsigned long iclr_base) | ||
820 | { | ||
821 | struct ino_bucket *bucket; | ||
822 | struct irq_handler_data *data; | ||
823 | unsigned long sysino; | ||
824 | unsigned int devino; | ||
825 | |||
826 | /* Find a free devino in the given range. */ | ||
827 | for (devino = msi_start; devino < msi_end; devino++) { | ||
828 | sysino = (portid << 6) | devino; | ||
829 | bucket = &ivector_table[sysino]; | ||
830 | if (!bucket->virt_irq) | ||
831 | break; | ||
832 | } | ||
833 | if (devino >= msi_end) | ||
834 | return -ENOSPC; | ||
835 | |||
836 | sysino = (portid << 6) | devino; | ||
837 | bucket = &ivector_table[sysino]; | ||
838 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | ||
839 | *virt_irq_p = bucket->virt_irq; | ||
840 | set_irq_chip(bucket->virt_irq, &sun4u_msi); | ||
841 | |||
842 | data = get_irq_chip_data(bucket->virt_irq); | ||
843 | if (unlikely(data)) | ||
844 | return devino; | ||
845 | |||
846 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | ||
847 | if (unlikely(!data)) { | ||
848 | virt_irq_free(*virt_irq_p); | ||
849 | return -ENOMEM; | ||
850 | } | ||
851 | set_irq_chip_data(bucket->virt_irq, data); | ||
852 | |||
853 | data->imap = (imap_base + (devino * 0x8UL)); | ||
854 | data->iclr = (iclr_base + (devino * 0x8UL)); | ||
855 | |||
856 | return devino; | ||
857 | } | ||
858 | |||
859 | void sun4u_destroy_msi(unsigned int virt_irq) | ||
860 | { | ||
861 | virt_irq_free(virt_irq); | ||
862 | } | ||
863 | #endif | ||
864 | |||
865 | void ack_bad_irq(unsigned int virt_irq) | 681 | void ack_bad_irq(unsigned int virt_irq) |
866 | { | 682 | { |
867 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 683 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |