aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2007-12-20 23:39:22 -0500
committerJosh Boyer <jwboyer@linux.vnet.ibm.com>2007-12-23 14:12:20 -0500
commit5738ec6d00b7abbcd4cd342af83fd18d192b0979 (patch)
treeed21eb9460c54feba4722d1524e919664be24c72 /arch
parent0e6140a56f2878816ecf9db50f40133d25d987e4 (diff)
[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>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/sysdev/Makefile3
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.c339
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.h106
3 files changed, 448 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 85cf8c60f0be..9a20ef4f0ea4 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -27,6 +27,9 @@ obj-$(CONFIG_PPC_I8259) += i8259.o
27obj-$(CONFIG_PPC_83xx) += ipic.o 27obj-$(CONFIG_PPC_83xx) += ipic.o
28obj-$(CONFIG_4xx) += uic.o 28obj-$(CONFIG_4xx) += uic.o
29obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o 29obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o
30ifeq ($(CONFIG_PCI),y)
31obj-$(CONFIG_4xx) += ppc4xx_pci.o
32endif
30endif 33endif
31 34
32# Temporary hack until we have migrated to asm-powerpc 35# Temporary hack until we have migrated to asm-powerpc
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
new file mode 100644
index 000000000000..a0b62398bc62
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -0,0 +1,339 @@
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
19static int dma_offset_set;
20
21/* Move that to a useable header */
22extern unsigned long total_memory;
23
24static 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 */
129static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
130{
131 /* NYI */
132}
133
134/*
135 * 4xx PCI-X part
136 */
137
138static 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
195static 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
225static 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 */
320static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
321{
322 /* NYI */
323}
324
325static 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}
338arch_initcall(ppc4xx_pci_find_bridges);
339
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h
new file mode 100644
index 000000000000..f9b4e6f4af2d
--- /dev/null
+++ b/arch/powerpc/sysdev/ppc4xx_pci.h
@@ -0,0 +1,106 @@
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__ */