diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-05-02 20:31:36 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-05-07 01:44:06 -0400 |
commit | 861fe90656b8e20d750d73c57088dc52d316ce7b (patch) | |
tree | 3f7df274478242ecf9f4186637c9cc38f59b5b8a /arch/sparc64/kernel/prom.c | |
parent | 4cad69174f385c183b2bcb369fb4304d8624ab96 (diff) |
[SPARC64]: SUN4U PCI-E controller support.
Some minor refactoring in the generic code was necessary for
this:
1) This controller requires 8-byte access to the interrupt map
and clear register. They are 64-bits on all the other
SBUS and PCI controllers anyways, so this was easy to cure.
2) The IMAP register has a different layout and some bits that we
need to preserve, so use a read/modify/write when making
changes to the IMAP register in generic code.
3) Flushing the entire IOMMU TLB is best done with a single write
to a register on this PCI controller, add a iommu->iommu_flushinv
for this.
Still lacks MSI support, that will come later.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/prom.c')
-rw-r--r-- | arch/sparc64/kernel/prom.c | 89 |
1 files changed, 83 insertions, 6 deletions
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c index 5e1fcd05160d..6625ac8d15fe 100644 --- a/arch/sparc64/kernel/prom.c +++ b/arch/sparc64/kernel/prom.c | |||
@@ -386,11 +386,9 @@ static unsigned int psycho_irq_build(struct device_node *dp, | |||
386 | 386 | ||
387 | /* Now build the IRQ bucket. */ | 387 | /* Now build the IRQ bucket. */ |
388 | imap = controller_regs + imap_off; | 388 | imap = controller_regs + imap_off; |
389 | imap += 4; | ||
390 | 389 | ||
391 | iclr_off = psycho_iclr_offset(ino); | 390 | iclr_off = psycho_iclr_offset(ino); |
392 | iclr = controller_regs + iclr_off; | 391 | iclr = controller_regs + iclr_off; |
393 | iclr += 4; | ||
394 | 392 | ||
395 | if ((ino & 0x20) == 0) | 393 | if ((ino & 0x20) == 0) |
396 | inofixup = ino & 0x03; | 394 | inofixup = ino & 0x03; |
@@ -613,11 +611,9 @@ static unsigned int sabre_irq_build(struct device_node *dp, | |||
613 | 611 | ||
614 | /* Now build the IRQ bucket. */ | 612 | /* Now build the IRQ bucket. */ |
615 | imap = controller_regs + imap_off; | 613 | imap = controller_regs + imap_off; |
616 | imap += 4; | ||
617 | 614 | ||
618 | iclr_off = sabre_iclr_offset(ino); | 615 | iclr_off = sabre_iclr_offset(ino); |
619 | iclr = controller_regs + iclr_off; | 616 | iclr = controller_regs + iclr_off; |
620 | iclr += 4; | ||
621 | 617 | ||
622 | if ((ino & 0x20) == 0) | 618 | if ((ino & 0x20) == 0) |
623 | inofixup = ino & 0x03; | 619 | inofixup = ino & 0x03; |
@@ -679,13 +675,14 @@ static unsigned long schizo_iclr_offset(unsigned long ino) | |||
679 | static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs, | 675 | static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs, |
680 | unsigned int ino) | 676 | unsigned int ino) |
681 | { | 677 | { |
682 | return pbm_regs + schizo_iclr_offset(ino) + 4; | 678 | |
679 | return pbm_regs + schizo_iclr_offset(ino); | ||
683 | } | 680 | } |
684 | 681 | ||
685 | static unsigned long schizo_ino_to_imap(unsigned long pbm_regs, | 682 | static unsigned long schizo_ino_to_imap(unsigned long pbm_regs, |
686 | unsigned int ino) | 683 | unsigned int ino) |
687 | { | 684 | { |
688 | return pbm_regs + schizo_imap_offset(ino) + 4; | 685 | return pbm_regs + schizo_imap_offset(ino); |
689 | } | 686 | } |
690 | 687 | ||
691 | #define schizo_read(__reg) \ | 688 | #define schizo_read(__reg) \ |
@@ -848,6 +845,85 @@ static void pci_sun4v_irq_trans_init(struct device_node *dp) | |||
848 | dp->irq_trans->data = (void *) (unsigned long) | 845 | dp->irq_trans->data = (void *) (unsigned long) |
849 | ((regs->phys_addr >> 32UL) & 0x0fffffff); | 846 | ((regs->phys_addr >> 32UL) & 0x0fffffff); |
850 | } | 847 | } |
848 | |||
849 | struct fire_irq_data { | ||
850 | unsigned long pbm_regs; | ||
851 | u32 portid; | ||
852 | }; | ||
853 | |||
854 | #define FIRE_IMAP_BASE 0x001000 | ||
855 | #define FIRE_ICLR_BASE 0x001400 | ||
856 | |||
857 | static unsigned long fire_imap_offset(unsigned long ino) | ||
858 | { | ||
859 | return FIRE_IMAP_BASE + (ino * 8UL); | ||
860 | } | ||
861 | |||
862 | static unsigned long fire_iclr_offset(unsigned long ino) | ||
863 | { | ||
864 | return FIRE_ICLR_BASE + (ino * 8UL); | ||
865 | } | ||
866 | |||
867 | static unsigned long fire_ino_to_iclr(unsigned long pbm_regs, | ||
868 | unsigned int ino) | ||
869 | { | ||
870 | return pbm_regs + fire_iclr_offset(ino); | ||
871 | } | ||
872 | |||
873 | static unsigned long fire_ino_to_imap(unsigned long pbm_regs, | ||
874 | unsigned int ino) | ||
875 | { | ||
876 | return pbm_regs + fire_imap_offset(ino); | ||
877 | } | ||
878 | |||
879 | static unsigned int fire_irq_build(struct device_node *dp, | ||
880 | unsigned int ino, | ||
881 | void *_data) | ||
882 | { | ||
883 | struct fire_irq_data *irq_data = _data; | ||
884 | unsigned long pbm_regs = irq_data->pbm_regs; | ||
885 | unsigned long imap, iclr; | ||
886 | unsigned long int_ctrlr; | ||
887 | |||
888 | ino &= 0x3f; | ||
889 | |||
890 | /* Now build the IRQ bucket. */ | ||
891 | imap = fire_ino_to_imap(pbm_regs, ino); | ||
892 | iclr = fire_ino_to_iclr(pbm_regs, ino); | ||
893 | |||
894 | /* Set the interrupt controller number. */ | ||
895 | int_ctrlr = 1 << 6; | ||
896 | upa_writeq(int_ctrlr, imap); | ||
897 | |||
898 | /* The interrupt map registers do not have an INO field | ||
899 | * like other chips do. They return zero in the INO | ||
900 | * field, and the interrupt controller number is controlled | ||
901 | * in bits 6 thru 9. So in order for build_irq() to get | ||
902 | * the INO right we pass it in as part of the fixup | ||
903 | * which will get added to the map register zero value | ||
904 | * read by build_irq(). | ||
905 | */ | ||
906 | ino |= (irq_data->portid << 6); | ||
907 | ino -= int_ctrlr; | ||
908 | return build_irq(ino, iclr, imap); | ||
909 | } | ||
910 | |||
911 | static void fire_irq_trans_init(struct device_node *dp) | ||
912 | { | ||
913 | const struct linux_prom64_registers *regs; | ||
914 | struct fire_irq_data *irq_data; | ||
915 | |||
916 | dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); | ||
917 | dp->irq_trans->irq_build = fire_irq_build; | ||
918 | |||
919 | irq_data = prom_early_alloc(sizeof(struct fire_irq_data)); | ||
920 | |||
921 | regs = of_get_property(dp, "reg", NULL); | ||
922 | dp->irq_trans->data = irq_data; | ||
923 | |||
924 | irq_data->pbm_regs = regs[0].phys_addr; | ||
925 | irq_data->portid = of_getintprop_default(dp, "portid", 0); | ||
926 | } | ||
851 | #endif /* CONFIG_PCI */ | 927 | #endif /* CONFIG_PCI */ |
852 | 928 | ||
853 | #ifdef CONFIG_SBUS | 929 | #ifdef CONFIG_SBUS |
@@ -1069,6 +1145,7 @@ static struct irq_trans pci_irq_trans_table[] = { | |||
1069 | { "SUNW,tomatillo", tomatillo_irq_trans_init }, | 1145 | { "SUNW,tomatillo", tomatillo_irq_trans_init }, |
1070 | { "pci108e,a801", tomatillo_irq_trans_init }, | 1146 | { "pci108e,a801", tomatillo_irq_trans_init }, |
1071 | { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init }, | 1147 | { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init }, |
1148 | { "pciex108e,80f0", fire_irq_trans_init }, | ||
1072 | }; | 1149 | }; |
1073 | #endif | 1150 | #endif |
1074 | 1151 | ||