diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2007-12-19 22:55:02 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-12-20 00:18:14 -0500 |
commit | d0264ce796e4e3d77fdadf72d6625f8e6c1c96bd (patch) | |
tree | 3b06b866c224c978b04673103ec35373d49b2408 /arch/powerpc/platforms/powermac/pci.c | |
parent | 444532d44aa6bc4d6e3ca74d8ad99c36f3b4d9f0 (diff) |
[POWERPC] Improve resource setup of PowerMac G5 HT bridge
The device node for the HT bridge on G5s doesn't contain useful ranges.
We used to give it a bunch of the known PCI space and then punch a "hole"
in it based on where the AGP or PCIe region was. This reworks it to
use the actual register in the bridge that controls the decoding instead.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms/powermac/pci.c')
-rw-r--r-- | arch/powerpc/platforms/powermac/pci.c | 142 |
1 files changed, 64 insertions, 78 deletions
diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index 6c93e7c0da15..1c58db9d42cb 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c | |||
@@ -40,8 +40,6 @@ | |||
40 | static int has_uninorth; | 40 | static int has_uninorth; |
41 | #ifdef CONFIG_PPC64 | 41 | #ifdef CONFIG_PPC64 |
42 | static struct pci_controller *u3_agp; | 42 | static struct pci_controller *u3_agp; |
43 | static struct pci_controller *u4_pcie; | ||
44 | static struct pci_controller *u3_ht; | ||
45 | #else | 43 | #else |
46 | static int has_second_ohare; | 44 | static int has_second_ohare; |
47 | #endif /* CONFIG_PPC64 */ | 45 | #endif /* CONFIG_PPC64 */ |
@@ -779,16 +777,50 @@ static void __init setup_u4_pcie(struct pci_controller* hose) | |||
779 | */ | 777 | */ |
780 | hose->first_busno = 0x00; | 778 | hose->first_busno = 0x00; |
781 | hose->last_busno = 0xff; | 779 | hose->last_busno = 0xff; |
782 | u4_pcie = hose; | 780 | } |
781 | |||
782 | static void __init parse_region_decode(struct pci_controller *hose, | ||
783 | u32 decode) | ||
784 | { | ||
785 | unsigned long base, end, next = -1; | ||
786 | int i, cur = -1; | ||
787 | |||
788 | /* Iterate through all bits. We ignore the last bit as this region is | ||
789 | * reserved for the ROM among other niceties | ||
790 | */ | ||
791 | for (i = 0; i < 31; i++) { | ||
792 | if ((decode & (0x80000000 >> i)) == 0) | ||
793 | continue; | ||
794 | if (i < 16) { | ||
795 | base = 0xf0000000 | (((u32)i) << 24); | ||
796 | end = base + 0x00ffffff; | ||
797 | } else { | ||
798 | base = ((u32)i-16) << 28; | ||
799 | end = base + 0x0fffffff; | ||
800 | } | ||
801 | if (base != next) { | ||
802 | if (++cur >= 3) { | ||
803 | printk(KERN_WARNING "PCI: Too many ranges !\n"); | ||
804 | break; | ||
805 | } | ||
806 | hose->mem_resources[cur].flags = IORESOURCE_MEM; | ||
807 | hose->mem_resources[cur].name = hose->dn->full_name; | ||
808 | hose->mem_resources[cur].start = base; | ||
809 | hose->mem_resources[cur].end = end; | ||
810 | DBG(" %d: 0x%08lx-0x%08lx\n", cur, base, end); | ||
811 | } else { | ||
812 | DBG(" : -0x%08lx\n", end); | ||
813 | hose->mem_resources[cur].end = end; | ||
814 | } | ||
815 | next = end + 1; | ||
816 | } | ||
783 | } | 817 | } |
784 | 818 | ||
785 | static void __init setup_u3_ht(struct pci_controller* hose) | 819 | static void __init setup_u3_ht(struct pci_controller* hose) |
786 | { | 820 | { |
787 | struct device_node *np = hose->dn; | 821 | struct device_node *np = hose->dn; |
788 | struct pci_controller *other = NULL; | ||
789 | struct resource cfg_res, self_res; | 822 | struct resource cfg_res, self_res; |
790 | int i, cur; | 823 | u32 decode; |
791 | |||
792 | 824 | ||
793 | hose->ops = &u3_ht_pci_ops; | 825 | hose->ops = &u3_ht_pci_ops; |
794 | 826 | ||
@@ -808,12 +840,9 @@ static void __init setup_u3_ht(struct pci_controller* hose) | |||
808 | self_res.end - self_res.start + 1); | 840 | self_res.end - self_res.start + 1); |
809 | 841 | ||
810 | /* | 842 | /* |
811 | * /ht node doesn't expose a "ranges" property, so we "remove" | 843 | * /ht node doesn't expose a "ranges" property, we read the register |
812 | * regions that have been allocated to AGP. So far, this version of | 844 | * that controls the decoding logic and use that for memory regions. |
813 | * the code doesn't assign any of the 0xfxxxxxxx "fine" memory regions | 845 | * The IO region is hard coded since it is fixed in HW as well. |
814 | * to /ht. We need to fix that sooner or later by either parsing all | ||
815 | * child "ranges" properties or figuring out the U3 address space | ||
816 | * decoding logic and then read its configuration register (if any). | ||
817 | */ | 846 | */ |
818 | hose->io_base_phys = 0xf4000000; | 847 | hose->io_base_phys = 0xf4000000; |
819 | hose->pci_io_size = 0x00400000; | 848 | hose->pci_io_size = 0x00400000; |
@@ -824,76 +853,33 @@ static void __init setup_u3_ht(struct pci_controller* hose) | |||
824 | hose->pci_mem_offset = 0; | 853 | hose->pci_mem_offset = 0; |
825 | hose->first_busno = 0; | 854 | hose->first_busno = 0; |
826 | hose->last_busno = 0xef; | 855 | hose->last_busno = 0xef; |
827 | hose->mem_resources[0].name = np->full_name; | ||
828 | hose->mem_resources[0].start = 0x80000000; | ||
829 | hose->mem_resources[0].end = 0xefffffff; | ||
830 | hose->mem_resources[0].flags = IORESOURCE_MEM; | ||
831 | |||
832 | u3_ht = hose; | ||
833 | 856 | ||
834 | if (u3_agp != NULL) | 857 | /* Note: fix offset when cfg_addr becomes a void * */ |
835 | other = u3_agp; | 858 | decode = in_be32(hose->cfg_addr + 0x80); |
836 | else if (u4_pcie != NULL) | ||
837 | other = u4_pcie; | ||
838 | |||
839 | if (other == NULL) { | ||
840 | DBG("U3/4 has no AGP/PCIE, using full resource range\n"); | ||
841 | return; | ||
842 | } | ||
843 | 859 | ||
844 | /* Fixup bus range vs. PCIE */ | 860 | DBG("PCI: Apple HT bridge decode register: 0x%08x\n", decode); |
845 | if (u4_pcie) | ||
846 | hose->last_busno = u4_pcie->first_busno - 1; | ||
847 | 861 | ||
848 | /* We "remove" the AGP resources from the resources allocated to HT, | 862 | /* NOTE: The decode register setup is a bit weird... region |
849 | * that is we create "holes". However, that code does assumptions | 863 | * 0xf8000000 for example is marked as enabled in there while it's |
850 | * that so far happen to be true (cross fingers...), typically that | 864 | & actually the memory controller registers. |
851 | * resources in the AGP node are properly ordered | 865 | * That means that we are incorrectly attributing it to HT. |
866 | * | ||
867 | * In a similar vein, region 0xf4000000 is actually the HT IO space but | ||
868 | * also marked as enabled in here and 0xf9000000 is used by some other | ||
869 | * internal bits of the northbridge. | ||
870 | * | ||
871 | * Unfortunately, we can't just mask out those bit as we would end | ||
872 | * up with more regions than we can cope (linux can only cope with | ||
873 | * 3 memory regions for a PHB at this stage). | ||
874 | * | ||
875 | * So for now, we just do a little hack. We happen to -know- that | ||
876 | * Apple firmware doesn't assign things below 0xfa000000 for that | ||
877 | * bridge anyway so we mask out all bits we don't want. | ||
852 | */ | 878 | */ |
853 | cur = 0; | 879 | decode &= 0x003fffff; |
854 | for (i=0; i<3; i++) { | 880 | |
855 | struct resource *res = &other->mem_resources[i]; | 881 | /* Now parse the resulting bits and build resources */ |
856 | if (res->flags != IORESOURCE_MEM) | 882 | parse_region_decode(hose, decode); |
857 | continue; | ||
858 | /* We don't care about "fine" resources */ | ||
859 | if (res->start >= 0xf0000000) | ||
860 | continue; | ||
861 | /* Check if it's just a matter of "shrinking" us in one | ||
862 | * direction | ||
863 | */ | ||
864 | if (hose->mem_resources[cur].start == res->start) { | ||
865 | DBG("U3/HT: shrink start of %d, %08lx -> %08lx\n", | ||
866 | cur, hose->mem_resources[cur].start, | ||
867 | res->end + 1); | ||
868 | hose->mem_resources[cur].start = res->end + 1; | ||
869 | continue; | ||
870 | } | ||
871 | if (hose->mem_resources[cur].end == res->end) { | ||
872 | DBG("U3/HT: shrink end of %d, %08lx -> %08lx\n", | ||
873 | cur, hose->mem_resources[cur].end, | ||
874 | res->start - 1); | ||
875 | hose->mem_resources[cur].end = res->start - 1; | ||
876 | continue; | ||
877 | } | ||
878 | /* No, it's not the case, we need a hole */ | ||
879 | if (cur == 2) { | ||
880 | /* not enough resources for a hole, we drop part | ||
881 | * of the range | ||
882 | */ | ||
883 | printk(KERN_WARNING "Running out of resources" | ||
884 | " for /ht host !\n"); | ||
885 | hose->mem_resources[cur].end = res->start - 1; | ||
886 | continue; | ||
887 | } | ||
888 | cur++; | ||
889 | DBG("U3/HT: hole, %d end at %08lx, %d start at %08lx\n", | ||
890 | cur-1, res->start - 1, cur, res->end + 1); | ||
891 | hose->mem_resources[cur].name = np->full_name; | ||
892 | hose->mem_resources[cur].flags = IORESOURCE_MEM; | ||
893 | hose->mem_resources[cur].start = res->end + 1; | ||
894 | hose->mem_resources[cur].end = hose->mem_resources[cur-1].end; | ||
895 | hose->mem_resources[cur-1].end = res->start - 1; | ||
896 | } | ||
897 | } | 883 | } |
898 | #endif /* CONFIG_PPC64 */ | 884 | #endif /* CONFIG_PPC64 */ |
899 | 885 | ||