diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2007-12-20 14:55:02 +1100 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-12-20 16:18:14 +1100 |
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 6c93e7c0da1..1c58db9d42c 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -40,8 +40,6 @@ static int has_uninorth; #ifdef CONFIG_PPC64 static struct pci_controller *u3_agp; -static struct pci_controller *u4_pcie; -static struct pci_controller *u3_ht; #else static int has_second_ohare; #endif /* CONFIG_PPC64 */ @@ -779,16 +777,50 @@ static void __init setup_u4_pcie(struct pci_controller* hose) */ hose->first_busno = 0x00; hose->last_busno = 0xff; - u4_pcie = hose; +} + +static void __init parse_region_decode(struct pci_controller *hose, + u32 decode) +{ + unsigned long base, end, next = -1; + int i, cur = -1; + + /* Iterate through all bits. We ignore the last bit as this region is + * reserved for the ROM among other niceties + */ + for (i = 0; i < 31; i++) { + if ((decode & (0x80000000 >> i)) == 0) + continue; + if (i < 16) { + base = 0xf0000000 | (((u32)i) << 24); + end = base + 0x00ffffff; + } else { + base = ((u32)i-16) << 28; + end = base + 0x0fffffff; + } + if (base != next) { + if (++cur >= 3) { + printk(KERN_WARNING "PCI: Too many ranges !\n"); + break; + } + hose->mem_resources[cur].flags = IORESOURCE_MEM; + hose->mem_resources[cur].name = hose->dn->full_name; + hose->mem_resources[cur].start = base; + hose->mem_resources[cur].end = end; + DBG(" %d: 0x%08lx-0x%08lx\n", cur, base, end); + } else { + DBG(" : -0x%08lx\n", end); + hose->mem_resources[cur].end = end; + } + next = end + 1; + } } static void __init setup_u3_ht(struct pci_controller* hose) { struct device_node *np = hose->dn; - struct pci_controller *other = NULL; struct resource cfg_res, self_res; - int i, cur; - + u32 decode; hose->ops = &u3_ht_pci_ops; @@ -808,12 +840,9 @@ static void __init setup_u3_ht(struct pci_controller* hose) self_res.end - self_res.start + 1); /* - * /ht node doesn't expose a "ranges" property, so we "remove" - * regions that have been allocated to AGP. So far, this version of - * the code doesn't assign any of the 0xfxxxxxxx "fine" memory regions - * to /ht. We need to fix that sooner or later by either parsing all - * child "ranges" properties or figuring out the U3 address space - * decoding logic and then read its configuration register (if any). + * /ht node doesn't expose a "ranges" property, we read the register + * that controls the decoding logic and use that for memory regions. + * The IO region is hard coded since it is fixed in HW as well. */ hose->io_base_phys = 0xf4000000; hose->pci_io_size = 0x00400000; @@ -824,76 +853,33 @@ static void __init setup_u3_ht(struct pci_controller* hose) hose->pci_mem_offset = 0; hose->first_busno = 0; hose->last_busno = 0xef; - hose->mem_resources[0].name = np->full_name; - hose->mem_resources[0].start = 0x80000000; - hose->mem_resources[0].end = 0xefffffff; - hose->mem_resources[0].flags = IORESOURCE_MEM; - - u3_ht = hose; - if (u3_agp != NULL) - other = u3_agp; - else if (u4_pcie != NULL) - other = u4_pcie; - - if (other == NULL) { - DBG("U3/4 has no AGP/PCIE, using full resource range\n"); - return; - } + /* Note: fix offset when cfg_addr becomes a void * */ + decode = in_be32(hose->cfg_addr + 0x80); - /* Fixup bus range vs. PCIE */ - if (u4_pcie) - hose->last_busno = u4_pcie->first_busno - 1; + DBG("PCI: Apple HT bridge decode register: 0x%08x\n", decode); - /* We "remove" the AGP resources from the resources allocated to HT, - * that is we create "holes". However, that code does assumptions - * that so far happen to be true (cross fingers...), typically that - * resources in the AGP node are properly ordered + /* NOTE: The decode register setup is a bit weird... region + * 0xf8000000 for example is marked as enabled in there while it's + & actually the memory controller registers. + * That means that we are incorrectly attributing it to HT. + * + * In a similar vein, region 0xf4000000 is actually the HT IO space but + * also marked as enabled in here and 0xf9000000 is used by some other + * internal bits of the northbridge. + * + * Unfortunately, we can't just mask out those bit as we would end + * up with more regions than we can cope (linux can only cope with + * 3 memory regions for a PHB at this stage). + * + * So for now, we just do a little hack. We happen to -know- that + * Apple firmware doesn't assign things below 0xfa000000 for that + * bridge anyway so we mask out all bits we don't want. */ - cur = 0; - for (i=0; i<3; i++) { - struct resource *res = &other->mem_resources[i]; - if (res->flags != IORESOURCE_MEM) - continue; - /* We don't care about "fine" resources */ - if (res->start >= 0xf0000000) - continue; - /* Check if it's just a matter of "shrinking" us in one - * direction - */ - if (hose->mem_resources[cur].start == res->start) { - DBG("U3/HT: shrink start of %d, %08lx -> %08lx\n", - cur, hose->mem_resources[cur].start, - res->end + 1); - hose->mem_resources[cur].start = res->end + 1; - continue; - } - if (hose->mem_resources[cur].end == res->end) { - DBG("U3/HT: shrink end of %d, %08lx -> %08lx\n", - cur, hose->mem_resources[cur].end, - res->start - 1); - hose->mem_resources[cur].end = res->start - 1; - continue; - } - /* No, it's not the case, we need a hole */ - if (cur == 2) { - /* not enough resources for a hole, we drop part - * of the range - */ - printk(KERN_WARNING "Running out of resources" - " for /ht host !\n"); - hose->mem_resources[cur].end = res->start - 1; - continue; - } - cur++; - DBG("U3/HT: hole, %d end at %08lx, %d start at %08lx\n", - cur-1, res->start - 1, cur, res->end + 1); - hose->mem_resources[cur].name = np->full_name; - hose->mem_resources[cur].flags = IORESOURCE_MEM; - hose->mem_resources[cur].start = res->end + 1; - hose->mem_resources[cur].end = hose->mem_resources[cur-1].end; - hose->mem_resources[cur-1].end = res->start - 1; - } + decode &= 0x003fffff; + + /* Now parse the resulting bits and build resources */ + parse_region_decode(hose, decode); } #endif /* CONFIG_PPC64 */ |