123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- /*
- * PCI / PCI-X / PCI-Express support for 4xx parts
- *
- * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
- *
- */
- #include <linux/kernel.h>
- #include <linux/pci.h>
- #include <linux/init.h>
- #include <linux/of.h>
- #include <asm/io.h>
- #include <asm/pci-bridge.h>
- #include <asm/machdep.h>
- #include "ppc4xx_pci.h"
- static int dma_offset_set;
- /* Move that to a useable header */
- extern unsigned long total_memory;
- static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
- void __iomem *reg,
- struct resource *res)
- {
- u64 size;
- const u32 *ranges;
- int rlen;
- int pna = of_n_addr_cells(hose->dn);
- int np = pna + 5;
- /* Default */
- res->start = 0;
- res->end = size = 0x80000000;
- res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
- /* Get dma-ranges property */
- ranges = of_get_property(hose->dn, "dma-ranges", &rlen);
- if (ranges == NULL)
- goto out;
- /* Walk it */
- while ((rlen -= np * 4) >= 0) {
- u32 pci_space = ranges[0];
- u64 pci_addr = of_read_number(ranges + 1, 2);
- u64 cpu_addr = of_translate_dma_address(hose->dn, ranges + 3);
- size = of_read_number(ranges + pna + 3, 2);
- ranges += np;
- if (cpu_addr == OF_BAD_ADDR || size == 0)
- continue;
- /* We only care about memory */
- if ((pci_space & 0x03000000) != 0x02000000)
- continue;
- /* We currently only support memory at 0, and pci_addr
- * within 32 bits space
- */
- if (cpu_addr != 0 || pci_addr > 0xffffffff) {
- printk(KERN_WARNING "%s: Ignored unsupported dma range"
- " 0x%016llx...0x%016llx -> 0x%016llx\n",
- hose->dn->full_name,
- pci_addr, pci_addr + size - 1, cpu_addr);
- continue;
- }
- /* Check if not prefetchable */
- if (!(pci_space & 0x40000000))
- res->flags &= ~IORESOURCE_PREFETCH;
- /* Use that */
- res->start = pci_addr;
- #ifndef CONFIG_RESOURCES_64BIT
- /* Beware of 32 bits resources */
- if ((pci_addr + size) > 0x100000000ull)
- res->end = 0xffffffff;
- else
- #endif
- res->end = res->start + size - 1;
- break;
- }
- /* We only support one global DMA offset */
- if (dma_offset_set && pci_dram_offset != res->start) {
- printk(KERN_ERR "%s: dma-ranges(s) mismatch\n",
- hose->dn->full_name);
- return -ENXIO;
- }
- /* Check that we can fit all of memory as we don't support
- * DMA bounce buffers
- */
- if (size < total_memory) {
- printk(KERN_ERR "%s: dma-ranges too small "
- "(size=%llx total_memory=%lx)\n",
- hose->dn->full_name, size, total_memory);
- return -ENXIO;
- }
- /* Check we are a power of 2 size and that base is a multiple of size*/
- if (!is_power_of_2(size) ||
- (res->start & (size - 1)) != 0) {
- printk(KERN_ERR "%s: dma-ranges unaligned\n",
- hose->dn->full_name);
- return -ENXIO;
- }
- /* Check that we are fully contained within 32 bits space */
- if (res->end > 0xffffffff) {
- printk(KERN_ERR "%s: dma-ranges outside of 32 bits space\n",
- hose->dn->full_name);
- return -ENXIO;
- }
- out:
- dma_offset_set = 1;
- pci_dram_offset = res->start;
- printk(KERN_INFO "4xx PCI DMA offset set to 0x%08lx\n",
- pci_dram_offset);
- return 0;
- }
- /*
- * 4xx PCI 2.x part
- */
- static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
- {
- /* NYI */
- }
- /*
- * 4xx PCI-X part
- */
- static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
- void __iomem *reg)
- {
- u32 lah, lal, pciah, pcial, sa;
- 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 > 1) {
- printk(KERN_WARNING "%s: Too many ranges\n",
- hose->dn->full_name);
- break;
- }
- /* Calculate register values */
- #ifdef CONFIG_PTE_64BIT
- lah = res->start >> 32;
- lal = res->start & 0xffffffffu;
- pciah = (res->start - hose->pci_mem_offset) >> 32;
- pcial = (res->start - hose->pci_mem_offset) & 0xffffffffu;
- #else
- lah = pciah = 0;
- lal = res->start;
- pcial = res->start - hose->pci_mem_offset;
- #endif
- sa = res->end + 1 - res->start;
- if (!is_power_of_2(sa) || sa < 0x100000 ||
- sa > 0xffffffffu) {
- printk(KERN_WARNING "%s: Resource out of range\n",
- hose->dn->full_name);
- continue;
- }
- sa = (0xffffffffu << ilog2(sa)) | 0x1;
- /* Program register values */
- if (j == 0) {
- writel(lah, reg + PCIX0_POM0LAH);
- writel(lal, reg + PCIX0_POM0LAL);
- writel(pciah, reg + PCIX0_POM0PCIAH);
- writel(pcial, reg + PCIX0_POM0PCIAL);
- writel(sa, reg + PCIX0_POM0SA);
- } else {
- writel(lah, reg + PCIX0_POM1LAH);
- writel(lal, reg + PCIX0_POM1LAL);
- writel(pciah, reg + PCIX0_POM1PCIAH);
- writel(pcial, reg + PCIX0_POM1PCIAL);
- writel(sa, reg + PCIX0_POM1SA);
- }
- j++;
- }
- }
- static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose,
- void __iomem *reg,
- const struct resource *res,
- int big_pim,
- int enable_msi_hole)
- {
- resource_size_t size = res->end - res->start + 1;
- u32 sa;
- /* RAM is always at 0 */
- writel(0x00000000, reg + PCIX0_PIM0LAH);
- writel(0x00000000, reg + PCIX0_PIM0LAL);
- /* Calculate window size */
- sa = (0xffffffffu << ilog2(size)) | 1;
- sa |= 0x1;
- if (res->flags & IORESOURCE_PREFETCH)
- sa |= 0x2;
- if (enable_msi_hole)
- sa |= 0x4;
- writel(sa, reg + PCIX0_PIM0SA);
- if (big_pim)
- writel(0xffffffff, reg + PCIX0_PIM0SAH);
- /* Map on PCI side */
- writel(0x00000000, reg + PCIX0_BAR0H);
- writel(res->start, reg + PCIX0_BAR0L);
- writew(0x0006, reg + PCIX0_COMMAND);
- }
- static void __init ppc4xx_probe_pcix_bridge(struct device_node *np)
- {
- 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 big_pim = 0, msi = 0, primary = 0;
- /* Fetch config space registers address */
- if (of_address_to_resource(np, 0, &rsrc_cfg)) {
- printk(KERN_ERR "%s:Can't get PCI-X 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-X internal register base !",
- np->full_name);
- return;
- }
- /* Check if it supports large PIMs (440GX) */
- if (of_get_property(np, "large-inbound-windows", NULL))
- big_pim = 1;
- /* Check if we should enable MSIs inbound hole */
- if (of_get_property(np, "enable-msi-hole", NULL))
- msi = 1;
- /* 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 + PCIX0_POM0SA);
- writel(0, reg + PCIX0_POM1SA);
- writel(0, reg + PCIX0_POM2SA);
- writel(0, reg + PCIX0_PIM0SA);
- writel(0, reg + PCIX0_PIM1SA);
- writel(0, reg + PCIX0_PIM2SA);
- if (big_pim) {
- writel(0, reg + PCIX0_PIM0SAH);
- writel(0, reg + PCIX0_PIM2SAH);
- }
- /* 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_pcix_POMs(hose, reg);
- /* Configure inbound ranges PIMs */
- ppc4xx_configure_pcix_PIMs(hose, reg, &dma_window, big_pim, msi);
- /* We don't need the registers anymore */
- iounmap(reg);
- return;
- fail:
- if (hose)
- pcibios_free_controller(hose);
- if (reg)
- iounmap(reg);
- }
- /*
- * 4xx PCI-Express part
- */
- static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
- {
- /* NYI */
- }
- static int __init ppc4xx_pci_find_bridges(void)
- {
- struct device_node *np;
- for_each_compatible_node(np, NULL, "ibm,plb-pciex")
- ppc4xx_probe_pciex_bridge(np);
- for_each_compatible_node(np, NULL, "ibm,plb-pcix")
- ppc4xx_probe_pcix_bridge(np);
- for_each_compatible_node(np, NULL, "ibm,plb-pci")
- ppc4xx_probe_pci_bridge(np);
- return 0;
- }
- arch_initcall(ppc4xx_pci_find_bridges);
|