Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[POWERPC] 4xx: PLB to PCI-X support

This adds base support code for the 4xx PCI-X bridge. It also provides
placeholders for the PCI and PCI-E version but they aren't supported
with this patch.

The bridges are configured based on device-tree properties.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>

authored by

Benjamin Herrenschmidt and committed by
Josh Boyer
5738ec6d 0e6140a5

+448
+3
arch/powerpc/sysdev/Makefile
··· 27 27 obj-$(CONFIG_PPC_83xx) += ipic.o 28 28 obj-$(CONFIG_4xx) += uic.o 29 29 obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o 30 + ifeq ($(CONFIG_PCI),y) 31 + obj-$(CONFIG_4xx) += ppc4xx_pci.o 32 + endif 30 33 endif 31 34 32 35 # Temporary hack until we have migrated to asm-powerpc
+339
arch/powerpc/sysdev/ppc4xx_pci.c
··· 1 + /* 2 + * PCI / PCI-X / PCI-Express support for 4xx parts 3 + * 4 + * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. 5 + * 6 + */ 7 + 8 + #include <linux/kernel.h> 9 + #include <linux/pci.h> 10 + #include <linux/init.h> 11 + #include <linux/of.h> 12 + 13 + #include <asm/io.h> 14 + #include <asm/pci-bridge.h> 15 + #include <asm/machdep.h> 16 + 17 + #include "ppc4xx_pci.h" 18 + 19 + static int dma_offset_set; 20 + 21 + /* Move that to a useable header */ 22 + extern unsigned long total_memory; 23 + 24 + static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose, 25 + void __iomem *reg, 26 + struct resource *res) 27 + { 28 + u64 size; 29 + const u32 *ranges; 30 + int rlen; 31 + int pna = of_n_addr_cells(hose->dn); 32 + int np = pna + 5; 33 + 34 + /* Default */ 35 + res->start = 0; 36 + res->end = size = 0x80000000; 37 + res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; 38 + 39 + /* Get dma-ranges property */ 40 + ranges = of_get_property(hose->dn, "dma-ranges", &rlen); 41 + if (ranges == NULL) 42 + goto out; 43 + 44 + /* Walk it */ 45 + while ((rlen -= np * 4) >= 0) { 46 + u32 pci_space = ranges[0]; 47 + u64 pci_addr = of_read_number(ranges + 1, 2); 48 + u64 cpu_addr = of_translate_dma_address(hose->dn, ranges + 3); 49 + size = of_read_number(ranges + pna + 3, 2); 50 + ranges += np; 51 + if (cpu_addr == OF_BAD_ADDR || size == 0) 52 + continue; 53 + 54 + /* We only care about memory */ 55 + if ((pci_space & 0x03000000) != 0x02000000) 56 + continue; 57 + 58 + /* We currently only support memory at 0, and pci_addr 59 + * within 32 bits space 60 + */ 61 + if (cpu_addr != 0 || pci_addr > 0xffffffff) { 62 + printk(KERN_WARNING "%s: Ignored unsupported dma range" 63 + " 0x%016llx...0x%016llx -> 0x%016llx\n", 64 + hose->dn->full_name, 65 + pci_addr, pci_addr + size - 1, cpu_addr); 66 + continue; 67 + } 68 + 69 + /* Check if not prefetchable */ 70 + if (!(pci_space & 0x40000000)) 71 + res->flags &= ~IORESOURCE_PREFETCH; 72 + 73 + 74 + /* Use that */ 75 + res->start = pci_addr; 76 + #ifndef CONFIG_RESOURCES_64BIT 77 + /* Beware of 32 bits resources */ 78 + if ((pci_addr + size) > 0x100000000ull) 79 + res->end = 0xffffffff; 80 + else 81 + #endif 82 + res->end = res->start + size - 1; 83 + break; 84 + } 85 + 86 + /* We only support one global DMA offset */ 87 + if (dma_offset_set && pci_dram_offset != res->start) { 88 + printk(KERN_ERR "%s: dma-ranges(s) mismatch\n", 89 + hose->dn->full_name); 90 + return -ENXIO; 91 + } 92 + 93 + /* Check that we can fit all of memory as we don't support 94 + * DMA bounce buffers 95 + */ 96 + if (size < total_memory) { 97 + printk(KERN_ERR "%s: dma-ranges too small " 98 + "(size=%llx total_memory=%lx)\n", 99 + hose->dn->full_name, size, total_memory); 100 + return -ENXIO; 101 + } 102 + 103 + /* Check we are a power of 2 size and that base is a multiple of size*/ 104 + if (!is_power_of_2(size) || 105 + (res->start & (size - 1)) != 0) { 106 + printk(KERN_ERR "%s: dma-ranges unaligned\n", 107 + hose->dn->full_name); 108 + return -ENXIO; 109 + } 110 + 111 + /* Check that we are fully contained within 32 bits space */ 112 + if (res->end > 0xffffffff) { 113 + printk(KERN_ERR "%s: dma-ranges outside of 32 bits space\n", 114 + hose->dn->full_name); 115 + return -ENXIO; 116 + } 117 + out: 118 + dma_offset_set = 1; 119 + pci_dram_offset = res->start; 120 + 121 + printk(KERN_INFO "4xx PCI DMA offset set to 0x%08lx\n", 122 + pci_dram_offset); 123 + return 0; 124 + } 125 + 126 + /* 127 + * 4xx PCI 2.x part 128 + */ 129 + static void __init ppc4xx_probe_pci_bridge(struct device_node *np) 130 + { 131 + /* NYI */ 132 + } 133 + 134 + /* 135 + * 4xx PCI-X part 136 + */ 137 + 138 + static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose, 139 + void __iomem *reg) 140 + { 141 + u32 lah, lal, pciah, pcial, sa; 142 + int i, j; 143 + 144 + /* Setup outbound memory windows */ 145 + for (i = j = 0; i < 3; i++) { 146 + struct resource *res = &hose->mem_resources[i]; 147 + 148 + /* we only care about memory windows */ 149 + if (!(res->flags & IORESOURCE_MEM)) 150 + continue; 151 + if (j > 1) { 152 + printk(KERN_WARNING "%s: Too many ranges\n", 153 + hose->dn->full_name); 154 + break; 155 + } 156 + 157 + /* Calculate register values */ 158 + #ifdef CONFIG_PTE_64BIT 159 + lah = res->start >> 32; 160 + lal = res->start & 0xffffffffu; 161 + pciah = (res->start - hose->pci_mem_offset) >> 32; 162 + pcial = (res->start - hose->pci_mem_offset) & 0xffffffffu; 163 + #else 164 + lah = pciah = 0; 165 + lal = res->start; 166 + pcial = res->start - hose->pci_mem_offset; 167 + #endif 168 + sa = res->end + 1 - res->start; 169 + if (!is_power_of_2(sa) || sa < 0x100000 || 170 + sa > 0xffffffffu) { 171 + printk(KERN_WARNING "%s: Resource out of range\n", 172 + hose->dn->full_name); 173 + continue; 174 + } 175 + sa = (0xffffffffu << ilog2(sa)) | 0x1; 176 + 177 + /* Program register values */ 178 + if (j == 0) { 179 + writel(lah, reg + PCIX0_POM0LAH); 180 + writel(lal, reg + PCIX0_POM0LAL); 181 + writel(pciah, reg + PCIX0_POM0PCIAH); 182 + writel(pcial, reg + PCIX0_POM0PCIAL); 183 + writel(sa, reg + PCIX0_POM0SA); 184 + } else { 185 + writel(lah, reg + PCIX0_POM1LAH); 186 + writel(lal, reg + PCIX0_POM1LAL); 187 + writel(pciah, reg + PCIX0_POM1PCIAH); 188 + writel(pcial, reg + PCIX0_POM1PCIAL); 189 + writel(sa, reg + PCIX0_POM1SA); 190 + } 191 + j++; 192 + } 193 + } 194 + 195 + static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose, 196 + void __iomem *reg, 197 + const struct resource *res, 198 + int big_pim, 199 + int enable_msi_hole) 200 + { 201 + resource_size_t size = res->end - res->start + 1; 202 + u32 sa; 203 + 204 + /* RAM is always at 0 */ 205 + writel(0x00000000, reg + PCIX0_PIM0LAH); 206 + writel(0x00000000, reg + PCIX0_PIM0LAL); 207 + 208 + /* Calculate window size */ 209 + sa = (0xffffffffu << ilog2(size)) | 1; 210 + sa |= 0x1; 211 + if (res->flags & IORESOURCE_PREFETCH) 212 + sa |= 0x2; 213 + if (enable_msi_hole) 214 + sa |= 0x4; 215 + writel(sa, reg + PCIX0_PIM0SA); 216 + if (big_pim) 217 + writel(0xffffffff, reg + PCIX0_PIM0SAH); 218 + 219 + /* Map on PCI side */ 220 + writel(0x00000000, reg + PCIX0_BAR0H); 221 + writel(res->start, reg + PCIX0_BAR0L); 222 + writew(0x0006, reg + PCIX0_COMMAND); 223 + } 224 + 225 + static void __init ppc4xx_probe_pcix_bridge(struct device_node *np) 226 + { 227 + struct resource rsrc_cfg; 228 + struct resource rsrc_reg; 229 + struct resource dma_window; 230 + struct pci_controller *hose = NULL; 231 + void __iomem *reg = NULL; 232 + const int *bus_range; 233 + int big_pim = 0, msi = 0, primary = 0; 234 + 235 + /* Fetch config space registers address */ 236 + if (of_address_to_resource(np, 0, &rsrc_cfg)) { 237 + printk(KERN_ERR "%s:Can't get PCI-X config register base !", 238 + np->full_name); 239 + return; 240 + } 241 + /* Fetch host bridge internal registers address */ 242 + if (of_address_to_resource(np, 3, &rsrc_reg)) { 243 + printk(KERN_ERR "%s: Can't get PCI-X internal register base !", 244 + np->full_name); 245 + return; 246 + } 247 + 248 + /* Check if it supports large PIMs (440GX) */ 249 + if (of_get_property(np, "large-inbound-windows", NULL)) 250 + big_pim = 1; 251 + 252 + /* Check if we should enable MSIs inbound hole */ 253 + if (of_get_property(np, "enable-msi-hole", NULL)) 254 + msi = 1; 255 + 256 + /* Check if primary bridge */ 257 + if (of_get_property(np, "primary", NULL)) 258 + primary = 1; 259 + 260 + /* Get bus range if any */ 261 + bus_range = of_get_property(np, "bus-range", NULL); 262 + 263 + /* Map registers */ 264 + reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start); 265 + if (reg == NULL) { 266 + printk(KERN_ERR "%s: Can't map registers !", np->full_name); 267 + goto fail; 268 + } 269 + 270 + /* Allocate the host controller data structure */ 271 + hose = pcibios_alloc_controller(np); 272 + if (!hose) 273 + goto fail; 274 + 275 + hose->first_busno = bus_range ? bus_range[0] : 0x0; 276 + hose->last_busno = bus_range ? bus_range[1] : 0xff; 277 + 278 + /* Setup config space */ 279 + setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0); 280 + 281 + /* Disable all windows */ 282 + writel(0, reg + PCIX0_POM0SA); 283 + writel(0, reg + PCIX0_POM1SA); 284 + writel(0, reg + PCIX0_POM2SA); 285 + writel(0, reg + PCIX0_PIM0SA); 286 + writel(0, reg + PCIX0_PIM1SA); 287 + writel(0, reg + PCIX0_PIM2SA); 288 + if (big_pim) { 289 + writel(0, reg + PCIX0_PIM0SAH); 290 + writel(0, reg + PCIX0_PIM2SAH); 291 + } 292 + 293 + /* Parse outbound mapping resources */ 294 + pci_process_bridge_OF_ranges(hose, np, primary); 295 + 296 + /* Parse inbound mapping resources */ 297 + if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0) 298 + goto fail; 299 + 300 + /* Configure outbound ranges POMs */ 301 + ppc4xx_configure_pcix_POMs(hose, reg); 302 + 303 + /* Configure inbound ranges PIMs */ 304 + ppc4xx_configure_pcix_PIMs(hose, reg, &dma_window, big_pim, msi); 305 + 306 + /* We don't need the registers anymore */ 307 + iounmap(reg); 308 + return; 309 + 310 + fail: 311 + if (hose) 312 + pcibios_free_controller(hose); 313 + if (reg) 314 + iounmap(reg); 315 + } 316 + 317 + /* 318 + * 4xx PCI-Express part 319 + */ 320 + static void __init ppc4xx_probe_pciex_bridge(struct device_node *np) 321 + { 322 + /* NYI */ 323 + } 324 + 325 + static int __init ppc4xx_pci_find_bridges(void) 326 + { 327 + struct device_node *np; 328 + 329 + for_each_compatible_node(np, NULL, "ibm,plb-pciex") 330 + ppc4xx_probe_pciex_bridge(np); 331 + for_each_compatible_node(np, NULL, "ibm,plb-pcix") 332 + ppc4xx_probe_pcix_bridge(np); 333 + for_each_compatible_node(np, NULL, "ibm,plb-pci") 334 + ppc4xx_probe_pci_bridge(np); 335 + 336 + return 0; 337 + } 338 + arch_initcall(ppc4xx_pci_find_bridges); 339 +
+106
arch/powerpc/sysdev/ppc4xx_pci.h
··· 1 + /* 2 + * PCI / PCI-X / PCI-Express support for 4xx parts 3 + * 4 + * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. 5 + * 6 + * Bits and pieces extracted from arch/ppc support by 7 + * 8 + * Matt Porter <mporter@kernel.crashing.org> 9 + * 10 + * Copyright 2002-2005 MontaVista Software Inc. 11 + */ 12 + #ifndef __PPC4XX_PCI_H__ 13 + #define __PPC4XX_PCI_H__ 14 + 15 + /* 16 + * 4xx PCI-X bridge register definitions 17 + */ 18 + #define PCIX0_VENDID 0x000 19 + #define PCIX0_DEVID 0x002 20 + #define PCIX0_COMMAND 0x004 21 + #define PCIX0_STATUS 0x006 22 + #define PCIX0_REVID 0x008 23 + #define PCIX0_CLS 0x009 24 + #define PCIX0_CACHELS 0x00c 25 + #define PCIX0_LATTIM 0x00d 26 + #define PCIX0_HDTYPE 0x00e 27 + #define PCIX0_BIST 0x00f 28 + #define PCIX0_BAR0L 0x010 29 + #define PCIX0_BAR0H 0x014 30 + #define PCIX0_BAR1 0x018 31 + #define PCIX0_BAR2L 0x01c 32 + #define PCIX0_BAR2H 0x020 33 + #define PCIX0_BAR3 0x024 34 + #define PCIX0_CISPTR 0x028 35 + #define PCIX0_SBSYSVID 0x02c 36 + #define PCIX0_SBSYSID 0x02e 37 + #define PCIX0_EROMBA 0x030 38 + #define PCIX0_CAP 0x034 39 + #define PCIX0_RES0 0x035 40 + #define PCIX0_RES1 0x036 41 + #define PCIX0_RES2 0x038 42 + #define PCIX0_INTLN 0x03c 43 + #define PCIX0_INTPN 0x03d 44 + #define PCIX0_MINGNT 0x03e 45 + #define PCIX0_MAXLTNCY 0x03f 46 + #define PCIX0_BRDGOPT1 0x040 47 + #define PCIX0_BRDGOPT2 0x044 48 + #define PCIX0_ERREN 0x050 49 + #define PCIX0_ERRSTS 0x054 50 + #define PCIX0_PLBBESR 0x058 51 + #define PCIX0_PLBBEARL 0x05c 52 + #define PCIX0_PLBBEARH 0x060 53 + #define PCIX0_POM0LAL 0x068 54 + #define PCIX0_POM0LAH 0x06c 55 + #define PCIX0_POM0SA 0x070 56 + #define PCIX0_POM0PCIAL 0x074 57 + #define PCIX0_POM0PCIAH 0x078 58 + #define PCIX0_POM1LAL 0x07c 59 + #define PCIX0_POM1LAH 0x080 60 + #define PCIX0_POM1SA 0x084 61 + #define PCIX0_POM1PCIAL 0x088 62 + #define PCIX0_POM1PCIAH 0x08c 63 + #define PCIX0_POM2SA 0x090 64 + #define PCIX0_PIM0SAL 0x098 65 + #define PCIX0_PIM0SA PCIX0_PIM0SAL 66 + #define PCIX0_PIM0LAL 0x09c 67 + #define PCIX0_PIM0LAH 0x0a0 68 + #define PCIX0_PIM1SA 0x0a4 69 + #define PCIX0_PIM1LAL 0x0a8 70 + #define PCIX0_PIM1LAH 0x0ac 71 + #define PCIX0_PIM2SAL 0x0b0 72 + #define PCIX0_PIM2SA PCIX0_PIM2SAL 73 + #define PCIX0_PIM2LAL 0x0b4 74 + #define PCIX0_PIM2LAH 0x0b8 75 + #define PCIX0_OMCAPID 0x0c0 76 + #define PCIX0_OMNIPTR 0x0c1 77 + #define PCIX0_OMMC 0x0c2 78 + #define PCIX0_OMMA 0x0c4 79 + #define PCIX0_OMMUA 0x0c8 80 + #define PCIX0_OMMDATA 0x0cc 81 + #define PCIX0_OMMEOI 0x0ce 82 + #define PCIX0_PMCAPID 0x0d0 83 + #define PCIX0_PMNIPTR 0x0d1 84 + #define PCIX0_PMC 0x0d2 85 + #define PCIX0_PMCSR 0x0d4 86 + #define PCIX0_PMCSRBSE 0x0d6 87 + #define PCIX0_PMDATA 0x0d7 88 + #define PCIX0_PMSCRR 0x0d8 89 + #define PCIX0_CAPID 0x0dc 90 + #define PCIX0_NIPTR 0x0dd 91 + #define PCIX0_CMD 0x0de 92 + #define PCIX0_STS 0x0e0 93 + #define PCIX0_IDR 0x0e4 94 + #define PCIX0_CID 0x0e8 95 + #define PCIX0_RID 0x0ec 96 + #define PCIX0_PIM0SAH 0x0f8 97 + #define PCIX0_PIM2SAH 0x0fc 98 + #define PCIX0_MSGIL 0x100 99 + #define PCIX0_MSGIH 0x104 100 + #define PCIX0_MSGOL 0x108 101 + #define PCIX0_MSGOH 0x10c 102 + #define PCIX0_IM 0x1f8 103 + 104 + 105 + 106 + #endif /* __PPC4XX_PCI_H__ */