diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2007-12-21 05:39:23 +0100 |
---|---|---|
committer | Josh Boyer <jwboyer@linux.vnet.ibm.com> | 2007-12-23 20:12:27 +0100 |
commit | c839e0eff500af03de65e560c2e21c3831586e6e (patch) | |
tree | d2a896b8e95e1ae9d314a9a3de4be18349bac0e7 /arch/powerpc/sysdev/ppc4xx_pci.c | |
parent | [POWERPC] 4xx: PLB to PCI-X support (diff) | |
download | linux-c839e0eff500af03de65e560c2e21c3831586e6e.tar.xz linux-c839e0eff500af03de65e560c2e21c3831586e6e.zip |
[POWERPC] 4xx: PLB to PCI 2.x support
This adds to the previous patch the support for the 4xx PCI 2.x
bridges.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Diffstat (limited to 'arch/powerpc/sysdev/ppc4xx_pci.c')
-rw-r--r-- | arch/powerpc/sysdev/ppc4xx_pci.c | 180 |
1 files changed, 179 insertions, 1 deletions
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index a0b62398bc62..b7d79880c9b9 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -21,6 +21,36 @@ static int dma_offset_set; /* Move that to a useable header */ extern unsigned long total_memory; +static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev) +{ + struct pci_controller *hose; + int i; + + if (dev->devfn != 0 || dev->bus->self != NULL) + return; + + hose = pci_bus_to_host(dev->bus); + if (hose == NULL) + return; + + if (!of_device_is_compatible(hose->dn, "ibm,plb-pciex") && + !of_device_is_compatible(hose->dn, "ibm,plb-pcix") && + !of_device_is_compatible(hose->dn, "ibm,plb-pci")) + return; + + /* Hide the PCI host BARs from the kernel as their content doesn't + * fit well in the resource management + */ + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + dev->resource[i].start = dev->resource[i].end = 0; + dev->resource[i].flags = 0; + } + + printk(KERN_INFO "PCI: Hiding 4xx host bridge resources %s\n", + pci_name(dev)); +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, fixup_ppc4xx_pci_bridge); + static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose, void __iomem *reg, struct resource *res) @@ -126,9 +156,157 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose, /* * 4xx PCI 2.x part */ + +static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose, + void __iomem *reg) +{ + u32 la, ma, pcila, pciha; + int i, j; + + /* Setup outbound memory windows */ + for (i = j = 0; i < 3; i++) { + struct resource *res = &hose->mem_resources[i]; + + /* we only care about memory windows */ + if (!(res->flags & IORESOURCE_MEM)) + continue; + if (j > 2) { + printk(KERN_WARNING "%s: Too many ranges\n", + hose->dn->full_name); + break; + } + + /* Calculate register values */ + la = res->start; +#ifdef CONFIG_RESOURCES_64BIT + pciha = (res->start - hose->pci_mem_offset) >> 32; + pcila = (res->start - hose->pci_mem_offset) & 0xffffffffu; +#else + pciha = 0; + pcila = res->start - hose->pci_mem_offset; +#endif + + ma = res->end + 1 - res->start; + if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) { + printk(KERN_WARNING "%s: Resource out of range\n", + hose->dn->full_name); + continue; + } + ma = (0xffffffffu << ilog2(ma)) | 0x1; + if (res->flags & IORESOURCE_PREFETCH) + ma |= 0x2; + + /* Program register values */ + writel(la, reg + PCIL0_PMM0LA + (0x10 * j)); + writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * j)); + writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * j)); + writel(ma, reg + PCIL0_PMM0MA + (0x10 * j)); + j++; + } +} + +static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose, + void __iomem *reg, + const struct resource *res) +{ + resource_size_t size = res->end - res->start + 1; + u32 sa; + + /* Calculate window size */ + sa = (0xffffffffu << ilog2(size)) | 1; + sa |= 0x1; + + /* RAM is always at 0 local for now */ + writel(0, reg + PCIL0_PTM1LA); + writel(sa, reg + PCIL0_PTM1MS); + + /* Map on PCI side */ + early_write_config_dword(hose, hose->first_busno, 0, + PCI_BASE_ADDRESS_1, res->start); + early_write_config_dword(hose, hose->first_busno, 0, + PCI_BASE_ADDRESS_2, 0x00000000); + early_write_config_word(hose, hose->first_busno, 0, + PCI_COMMAND, 0x0006); +} + static void __init ppc4xx_probe_pci_bridge(struct device_node *np) { /* NYI */ + struct resource rsrc_cfg; + struct resource rsrc_reg; + struct resource dma_window; + struct pci_controller *hose = NULL; + void __iomem *reg = NULL; + const int *bus_range; + int primary = 0; + + /* Fetch config space registers address */ + if (of_address_to_resource(np, 0, &rsrc_cfg)) { + printk(KERN_ERR "%s:Can't get PCI config register base !", + np->full_name); + return; + } + /* Fetch host bridge internal registers address */ + if (of_address_to_resource(np, 3, &rsrc_reg)) { + printk(KERN_ERR "%s: Can't get PCI internal register base !", + np->full_name); + return; + } + + /* Check if primary bridge */ + if (of_get_property(np, "primary", NULL)) + primary = 1; + + /* Get bus range if any */ + bus_range = of_get_property(np, "bus-range", NULL); + + /* Map registers */ + reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start); + if (reg == NULL) { + printk(KERN_ERR "%s: Can't map registers !", np->full_name); + goto fail; + } + + /* Allocate the host controller data structure */ + hose = pcibios_alloc_controller(np); + if (!hose) + goto fail; + + hose->first_busno = bus_range ? bus_range[0] : 0x0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + /* Setup config space */ + setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0); + + /* Disable all windows */ + writel(0, reg + PCIL0_PMM0MA); + writel(0, reg + PCIL0_PMM1MA); + writel(0, reg + PCIL0_PMM2MA); + writel(0, reg + PCIL0_PTM1MS); + writel(0, reg + PCIL0_PTM2MS); + + /* Parse outbound mapping resources */ + pci_process_bridge_OF_ranges(hose, np, primary); + + /* Parse inbound mapping resources */ + if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0) + goto fail; + + /* Configure outbound ranges POMs */ + ppc4xx_configure_pci_PMMs(hose, reg); + + /* Configure inbound ranges PIMs */ + ppc4xx_configure_pci_PTMs(hose, reg, &dma_window); + + /* We don't need the registers anymore */ + iounmap(reg); + return; + + fail: + if (hose) + pcibios_free_controller(hose); + if (reg) + iounmap(reg); } /* @@ -155,7 +333,7 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose, } /* Calculate register values */ -#ifdef CONFIG_PTE_64BIT +#ifdef CONFIG_RESOURCES_64BIT lah = res->start >> 32; lal = res->start & 0xffffffffu; pciah = (res->start - hose->pci_mem_offset) >> 32; |