diff options
author | Ishizaki Kou <kou.ishizaki@toshiba.co.jp> | 2008-04-24 05:21:10 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-04-24 07:08:12 -0400 |
commit | 7cfb62a2e820b6032c08835dbd996d8518af14a3 (patch) | |
tree | fdcd728c96ca020214452f11ae8600525e9d3773 /arch/powerpc | |
parent | 36f8a2c4c61e3559a95190e457b431c6900859b4 (diff) |
[POWERPC] cell: Generalize io-workarounds code
This splits cell io-workaround code into spider-pci dependent code and
a generic part, and also moves io-workarounds initialization into
cell_setup_phb.
Signed-off-by: Kou Ishizaki <kou.ishizaki@toshiba.co.jp>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/kernel/of_platform.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/Makefile | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/io-workarounds.c | 358 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/io-workarounds.h | 49 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/setup.c | 43 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spider-pci.c | 184 |
6 files changed, 378 insertions, 261 deletions
diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index fb698d47082d..e79ad8afda07 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c | |||
@@ -275,6 +275,8 @@ static int __devinit of_pci_phb_probe(struct of_device *dev, | |||
275 | 275 | ||
276 | /* Scan the bus */ | 276 | /* Scan the bus */ |
277 | scan_phb(phb); | 277 | scan_phb(phb); |
278 | if (phb->bus == NULL) | ||
279 | return -ENXIO; | ||
278 | 280 | ||
279 | /* Claim resources. This might need some rework as well depending | 281 | /* Claim resources. This might need some rework as well depending |
280 | * wether we are doing probe-only or not, like assigning unassigned | 282 | * wether we are doing probe-only or not, like assigning unassigned |
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index c89964c6fb1f..20966b709604 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile | |||
@@ -1,6 +1,7 @@ | |||
1 | obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \ | 1 | obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \ |
2 | cbe_regs.o spider-pic.o \ | 2 | cbe_regs.o spider-pic.o \ |
3 | pervasive.o pmu.o io-workarounds.o | 3 | pervasive.o pmu.o io-workarounds.o \ |
4 | spider-pci.o | ||
4 | obj-$(CONFIG_CBE_RAS) += ras.o | 5 | obj-$(CONFIG_CBE_RAS) += ras.o |
5 | 6 | ||
6 | obj-$(CONFIG_CBE_THERM) += cbe_thermal.o | 7 | obj-$(CONFIG_CBE_THERM) += cbe_thermal.o |
diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c index 979d4b67efb4..3b84e8be314c 100644 --- a/arch/powerpc/platforms/cell/io-workarounds.c +++ b/arch/powerpc/platforms/cell/io-workarounds.c | |||
@@ -1,6 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * Support PCI IO workaround | ||
3 | * | ||
2 | * Copyright (C) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> | 4 | * Copyright (C) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org> |
3 | * IBM, Corp. | 5 | * IBM, Corp. |
6 | * (C) Copyright 2007-2008 TOSHIBA CORPORATION | ||
4 | * | 7 | * |
5 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,335 +12,174 @@ | |||
9 | #undef DEBUG | 12 | #undef DEBUG |
10 | 13 | ||
11 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
12 | #include <linux/mm.h> | 15 | |
13 | #include <linux/pci.h> | ||
14 | #include <asm/io.h> | 16 | #include <asm/io.h> |
15 | #include <asm/machdep.h> | 17 | #include <asm/machdep.h> |
16 | #include <asm/pci-bridge.h> | 18 | #include <asm/pgtable.h> |
17 | #include <asm/ppc-pci.h> | 19 | #include <asm/ppc-pci.h> |
18 | 20 | ||
21 | #include "io-workarounds.h" | ||
19 | 22 | ||
20 | #define SPIDER_PCI_REG_BASE 0xd000 | 23 | #define IOWA_MAX_BUS 8 |
21 | #define SPIDER_PCI_VCI_CNTL_STAT 0x0110 | ||
22 | #define SPIDER_PCI_DUMMY_READ 0x0810 | ||
23 | #define SPIDER_PCI_DUMMY_READ_BASE 0x0814 | ||
24 | 24 | ||
25 | /* Undefine that to re-enable bogus prefetch | 25 | static struct iowa_bus iowa_busses[IOWA_MAX_BUS]; |
26 | * | 26 | static unsigned int iowa_bus_count; |
27 | * Without that workaround, the chip will do bogus prefetch past | ||
28 | * page boundary from system memory. This setting will disable that, | ||
29 | * though the documentation is unclear as to the consequences of doing | ||
30 | * so, either purely performances, or possible misbehaviour... It's not | ||
31 | * clear wether the chip can handle unaligned accesses at all without | ||
32 | * prefetching enabled. | ||
33 | * | ||
34 | * For now, things appear to be behaving properly with that prefetching | ||
35 | * disabled and IDE, possibly because IDE isn't doing any unaligned | ||
36 | * access. | ||
37 | */ | ||
38 | #define SPIDER_DISABLE_PREFETCH | ||
39 | 27 | ||
40 | #define MAX_SPIDERS 3 | 28 | static struct iowa_bus *iowa_pci_find(unsigned long vaddr, unsigned long paddr) |
29 | { | ||
30 | int i, j; | ||
31 | struct resource *res; | ||
32 | unsigned long vstart, vend; | ||
41 | 33 | ||
42 | static struct spider_pci_bus { | 34 | for (i = 0; i < iowa_bus_count; i++) { |
43 | void __iomem *regs; | 35 | struct iowa_bus *bus = &iowa_busses[i]; |
44 | unsigned long mmio_start; | 36 | struct pci_controller *phb = bus->phb; |
45 | unsigned long mmio_end; | ||
46 | unsigned long pio_vstart; | ||
47 | unsigned long pio_vend; | ||
48 | } spider_pci_busses[MAX_SPIDERS]; | ||
49 | static int spider_pci_count; | ||
50 | 37 | ||
51 | static struct spider_pci_bus *spider_pci_find(unsigned long vaddr, | 38 | if (vaddr) { |
52 | unsigned long paddr) | 39 | vstart = (unsigned long)phb->io_base_virt; |
53 | { | 40 | vend = vstart + phb->pci_io_size - 1; |
54 | int i; | 41 | if ((vaddr >= vstart) && (vaddr <= vend)) |
55 | 42 | return bus; | |
56 | for (i = 0; i < spider_pci_count; i++) { | 43 | } |
57 | struct spider_pci_bus *bus = &spider_pci_busses[i]; | 44 | |
58 | if (paddr && paddr >= bus->mmio_start && paddr < bus->mmio_end) | 45 | if (paddr) |
59 | return bus; | 46 | for (j = 0; j < 3; j++) { |
60 | if (vaddr && vaddr >= bus->pio_vstart && vaddr < bus->pio_vend) | 47 | res = &phb->mem_resources[j]; |
61 | return bus; | 48 | if (paddr >= res->start && paddr <= res->end) |
49 | return bus; | ||
50 | } | ||
62 | } | 51 | } |
52 | |||
63 | return NULL; | 53 | return NULL; |
64 | } | 54 | } |
65 | 55 | ||
66 | static void spider_io_flush(const volatile void __iomem *addr) | 56 | struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr) |
67 | { | 57 | { |
68 | struct spider_pci_bus *bus; | 58 | struct iowa_bus *bus; |
69 | int token; | 59 | int token; |
70 | 60 | ||
71 | /* Get platform token (set by ioremap) from address */ | ||
72 | token = PCI_GET_ADDR_TOKEN(addr); | 61 | token = PCI_GET_ADDR_TOKEN(addr); |
73 | 62 | ||
74 | /* Fast path if we have a non-0 token, it indicates which bus we | 63 | if (token && token <= iowa_bus_count) |
75 | * are on. | 64 | bus = &iowa_busses[token - 1]; |
76 | * | ||
77 | * If the token is 0, that means either that the ioremap was done | ||
78 | * before we initialized this layer, or it's a PIO operation. We | ||
79 | * fallback to a low path in this case. Hopefully, internal devices | ||
80 | * which are ioremap'ed early should use in_XX/out_XX functions | ||
81 | * instead of the PCI ones and thus not suffer from the slowdown. | ||
82 | * | ||
83 | * Also note that currently, the workaround will not work for areas | ||
84 | * that are not mapped with PTEs (bolted in the hash table). This | ||
85 | * is the case for ioremaps done very early at boot (before | ||
86 | * mem_init_done) and includes the mapping of the ISA IO space. | ||
87 | * | ||
88 | * Fortunately, none of the affected devices is expected to do DMA | ||
89 | * and thus there should be no problem in practice. | ||
90 | * | ||
91 | * In order to improve performances, we only do the PTE search for | ||
92 | * addresses falling in the PHB IO space area. That means it will | ||
93 | * not work for hotplug'ed PHBs but those don't exist with Spider. | ||
94 | */ | ||
95 | if (token && token <= spider_pci_count) | ||
96 | bus = &spider_pci_busses[token - 1]; | ||
97 | else { | 65 | else { |
98 | unsigned long vaddr, paddr; | 66 | unsigned long vaddr, paddr; |
99 | pte_t *ptep; | 67 | pte_t *ptep; |
100 | 68 | ||
101 | /* Fixup physical address */ | ||
102 | vaddr = (unsigned long)PCI_FIX_ADDR(addr); | 69 | vaddr = (unsigned long)PCI_FIX_ADDR(addr); |
70 | if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END) | ||
71 | return NULL; | ||
103 | 72 | ||
104 | /* Check if it's in allowed range for PIO */ | ||
105 | if (vaddr < PHB_IO_BASE || vaddr > PHB_IO_END) | ||
106 | return; | ||
107 | |||
108 | /* Try to find a PTE. If not, clear the paddr, we'll do | ||
109 | * a vaddr only lookup (PIO only) | ||
110 | */ | ||
111 | ptep = find_linux_pte(init_mm.pgd, vaddr); | 73 | ptep = find_linux_pte(init_mm.pgd, vaddr); |
112 | if (ptep == NULL) | 74 | if (ptep == NULL) |
113 | paddr = 0; | 75 | paddr = 0; |
114 | else | 76 | else |
115 | paddr = pte_pfn(*ptep) << PAGE_SHIFT; | 77 | paddr = pte_pfn(*ptep) << PAGE_SHIFT; |
78 | bus = iowa_pci_find(vaddr, paddr); | ||
116 | 79 | ||
117 | bus = spider_pci_find(vaddr, paddr); | ||
118 | if (bus == NULL) | 80 | if (bus == NULL) |
119 | return; | 81 | return NULL; |
120 | } | 82 | } |
121 | 83 | ||
122 | /* Now do the workaround | 84 | return bus; |
123 | */ | ||
124 | (void)in_be32(bus->regs + SPIDER_PCI_DUMMY_READ); | ||
125 | } | 85 | } |
126 | 86 | ||
127 | static u8 spider_readb(const volatile void __iomem *addr) | 87 | struct iowa_bus *iowa_pio_find_bus(unsigned long port) |
128 | { | 88 | { |
129 | u8 val = __do_readb(addr); | 89 | unsigned long vaddr = (unsigned long)pci_io_base + port; |
130 | spider_io_flush(addr); | 90 | return iowa_pci_find(vaddr, 0); |
131 | return val; | ||
132 | } | 91 | } |
133 | 92 | ||
134 | static u16 spider_readw(const volatile void __iomem *addr) | ||
135 | { | ||
136 | u16 val = __do_readw(addr); | ||
137 | spider_io_flush(addr); | ||
138 | return val; | ||
139 | } | ||
140 | 93 | ||
141 | static u32 spider_readl(const volatile void __iomem *addr) | 94 | #define DEF_PCI_AC_RET(name, ret, at, al, space, aa) \ |
142 | { | 95 | static ret iowa_##name at \ |
143 | u32 val = __do_readl(addr); | 96 | { \ |
144 | spider_io_flush(addr); | 97 | struct iowa_bus *bus; \ |
145 | return val; | 98 | bus = iowa_##space##_find_bus(aa); \ |
99 | if (bus && bus->ops && bus->ops->name) \ | ||
100 | return bus->ops->name al; \ | ||
101 | return __do_##name al; \ | ||
146 | } | 102 | } |
147 | 103 | ||
148 | static u64 spider_readq(const volatile void __iomem *addr) | 104 | #define DEF_PCI_AC_NORET(name, at, al, space, aa) \ |
149 | { | 105 | static void iowa_##name at \ |
150 | u64 val = __do_readq(addr); | 106 | { \ |
151 | spider_io_flush(addr); | 107 | struct iowa_bus *bus; \ |
152 | return val; | 108 | bus = iowa_##space##_find_bus(aa); \ |
109 | if (bus && bus->ops && bus->ops->name) { \ | ||
110 | bus->ops->name al; \ | ||
111 | return; \ | ||
112 | } \ | ||
113 | __do_##name al; \ | ||
153 | } | 114 | } |
154 | 115 | ||
155 | static u16 spider_readw_be(const volatile void __iomem *addr) | 116 | #include <asm/io-defs.h> |
156 | { | ||
157 | u16 val = __do_readw_be(addr); | ||
158 | spider_io_flush(addr); | ||
159 | return val; | ||
160 | } | ||
161 | 117 | ||
162 | static u32 spider_readl_be(const volatile void __iomem *addr) | 118 | #undef DEF_PCI_AC_RET |
163 | { | 119 | #undef DEF_PCI_AC_NORET |
164 | u32 val = __do_readl_be(addr); | ||
165 | spider_io_flush(addr); | ||
166 | return val; | ||
167 | } | ||
168 | 120 | ||
169 | static u64 spider_readq_be(const volatile void __iomem *addr) | 121 | static struct ppc_pci_io __initdata iowa_pci_io = { |
170 | { | ||
171 | u64 val = __do_readq_be(addr); | ||
172 | spider_io_flush(addr); | ||
173 | return val; | ||
174 | } | ||
175 | 122 | ||
176 | static void spider_readsb(const volatile void __iomem *addr, void *buf, | 123 | #define DEF_PCI_AC_RET(name, ret, at, al, space, aa) .name = iowa_##name, |
177 | unsigned long count) | 124 | #define DEF_PCI_AC_NORET(name, at, al, space, aa) .name = iowa_##name, |
178 | { | ||
179 | __do_readsb(addr, buf, count); | ||
180 | spider_io_flush(addr); | ||
181 | } | ||
182 | 125 | ||
183 | static void spider_readsw(const volatile void __iomem *addr, void *buf, | 126 | #include <asm/io-defs.h> |
184 | unsigned long count) | ||
185 | { | ||
186 | __do_readsw(addr, buf, count); | ||
187 | spider_io_flush(addr); | ||
188 | } | ||
189 | 127 | ||
190 | static void spider_readsl(const volatile void __iomem *addr, void *buf, | 128 | #undef DEF_PCI_AC_RET |
191 | unsigned long count) | 129 | #undef DEF_PCI_AC_NORET |
192 | { | ||
193 | __do_readsl(addr, buf, count); | ||
194 | spider_io_flush(addr); | ||
195 | } | ||
196 | |||
197 | static void spider_memcpy_fromio(void *dest, const volatile void __iomem *src, | ||
198 | unsigned long n) | ||
199 | { | ||
200 | __do_memcpy_fromio(dest, src, n); | ||
201 | spider_io_flush(src); | ||
202 | } | ||
203 | 130 | ||
131 | }; | ||
204 | 132 | ||
205 | static void __iomem * spider_ioremap(unsigned long addr, unsigned long size, | 133 | static void __iomem *iowa_ioremap(unsigned long addr, unsigned long size, |
206 | unsigned long flags) | 134 | unsigned long flags) |
207 | { | 135 | { |
208 | struct spider_pci_bus *bus; | 136 | struct iowa_bus *bus; |
209 | void __iomem *res = __ioremap(addr, size, flags); | 137 | void __iomem *res = __ioremap(addr, size, flags); |
210 | int busno; | 138 | int busno; |
211 | 139 | ||
212 | pr_debug("spider_ioremap(0x%lx, 0x%lx, 0x%lx) -> 0x%p\n", | 140 | bus = iowa_pci_find(0, addr); |
213 | addr, size, flags, res); | ||
214 | |||
215 | bus = spider_pci_find(0, addr); | ||
216 | if (bus != NULL) { | 141 | if (bus != NULL) { |
217 | busno = bus - spider_pci_busses; | 142 | busno = bus - iowa_busses; |
218 | pr_debug(" found bus %d, setting token\n", busno); | ||
219 | PCI_SET_ADDR_TOKEN(res, busno + 1); | 143 | PCI_SET_ADDR_TOKEN(res, busno + 1); |
220 | } | 144 | } |
221 | pr_debug(" result=0x%p\n", res); | ||
222 | |||
223 | return res; | 145 | return res; |
224 | } | 146 | } |
225 | 147 | ||
226 | static void __init spider_pci_setup_chip(struct spider_pci_bus *bus) | 148 | /* Regist new bus to support workaround */ |
227 | { | 149 | void __init iowa_register_bus(struct pci_controller *phb, |
228 | #ifdef SPIDER_DISABLE_PREFETCH | 150 | struct ppc_pci_io *ops, |
229 | u32 val = in_be32(bus->regs + SPIDER_PCI_VCI_CNTL_STAT); | 151 | int (*initfunc)(struct iowa_bus *, void *), void *data) |
230 | pr_debug(" PVCI_Control_Status was 0x%08x\n", val); | ||
231 | out_be32(bus->regs + SPIDER_PCI_VCI_CNTL_STAT, val | 0x8); | ||
232 | #endif | ||
233 | |||
234 | /* Configure the dummy address for the workaround */ | ||
235 | out_be32(bus->regs + SPIDER_PCI_DUMMY_READ_BASE, 0x80000000); | ||
236 | } | ||
237 | |||
238 | static void __init spider_pci_add_one(struct pci_controller *phb) | ||
239 | { | 152 | { |
240 | struct spider_pci_bus *bus = &spider_pci_busses[spider_pci_count]; | 153 | struct iowa_bus *bus; |
241 | struct device_node *np = phb->dn; | 154 | struct device_node *np = phb->dn; |
242 | struct resource rsrc; | ||
243 | void __iomem *regs; | ||
244 | 155 | ||
245 | if (spider_pci_count >= MAX_SPIDERS) { | 156 | if (iowa_bus_count >= IOWA_MAX_BUS) { |
246 | printk(KERN_ERR "Too many spider bridges, workarounds" | 157 | pr_err("IOWA:Too many pci bridges, " |
247 | " disabled for %s\n", np->full_name); | 158 | "workarounds disabled for %s\n", np->full_name); |
248 | return; | 159 | return; |
249 | } | 160 | } |
250 | 161 | ||
251 | /* Get the registers for the beast */ | 162 | bus = &iowa_busses[iowa_bus_count]; |
252 | if (of_address_to_resource(np, 0, &rsrc)) { | 163 | bus->phb = phb; |
253 | printk(KERN_ERR "Failed to get registers for spider %s" | 164 | bus->ops = ops; |
254 | " workarounds disabled\n", np->full_name); | ||
255 | return; | ||
256 | } | ||
257 | 165 | ||
258 | /* Mask out some useless bits in there to get to the base of the | 166 | if (initfunc) |
259 | * spider chip | 167 | if ((*initfunc)(bus, data)) |
260 | */ | 168 | return; |
261 | rsrc.start &= ~0xfffffffful; | ||
262 | |||
263 | /* Map them */ | ||
264 | regs = ioremap(rsrc.start + SPIDER_PCI_REG_BASE, 0x1000); | ||
265 | if (regs == NULL) { | ||
266 | printk(KERN_ERR "Failed to map registers for spider %s" | ||
267 | " workarounds disabled\n", np->full_name); | ||
268 | return; | ||
269 | } | ||
270 | |||
271 | spider_pci_count++; | ||
272 | |||
273 | /* We assume spiders only have one MMIO resource */ | ||
274 | bus->mmio_start = phb->mem_resources[0].start; | ||
275 | bus->mmio_end = phb->mem_resources[0].end + 1; | ||
276 | |||
277 | bus->pio_vstart = (unsigned long)phb->io_base_virt; | ||
278 | bus->pio_vend = bus->pio_vstart + phb->pci_io_size; | ||
279 | |||
280 | bus->regs = regs; | ||
281 | |||
282 | printk(KERN_INFO "PCI: Spider MMIO workaround for %s\n",np->full_name); | ||
283 | 169 | ||
284 | pr_debug(" mmio (P) = 0x%016lx..0x%016lx\n", | 170 | iowa_bus_count++; |
285 | bus->mmio_start, bus->mmio_end); | ||
286 | pr_debug(" pio (V) = 0x%016lx..0x%016lx\n", | ||
287 | bus->pio_vstart, bus->pio_vend); | ||
288 | pr_debug(" regs (P) = 0x%016lx (V) = 0x%p\n", | ||
289 | rsrc.start + SPIDER_PCI_REG_BASE, bus->regs); | ||
290 | 171 | ||
291 | spider_pci_setup_chip(bus); | 172 | pr_debug("IOWA:[%d]Add bus, %s.\n", iowa_bus_count-1, np->full_name); |
292 | } | 173 | } |
293 | 174 | ||
294 | static struct ppc_pci_io __initdata spider_pci_io = { | 175 | /* enable IO workaround */ |
295 | .readb = spider_readb, | 176 | void __init io_workaround_init(void) |
296 | .readw = spider_readw, | ||
297 | .readl = spider_readl, | ||
298 | .readq = spider_readq, | ||
299 | .readw_be = spider_readw_be, | ||
300 | .readl_be = spider_readl_be, | ||
301 | .readq_be = spider_readq_be, | ||
302 | .readsb = spider_readsb, | ||
303 | .readsw = spider_readsw, | ||
304 | .readsl = spider_readsl, | ||
305 | .memcpy_fromio = spider_memcpy_fromio, | ||
306 | }; | ||
307 | |||
308 | static int __init spider_pci_workaround_init(void) | ||
309 | { | 177 | { |
310 | struct pci_controller *phb; | 178 | static int io_workaround_inited; |
311 | |||
312 | /* Find spider bridges. We assume they have been all probed | ||
313 | * in setup_arch(). If that was to change, we would need to | ||
314 | * update this code to cope with dynamically added busses | ||
315 | */ | ||
316 | list_for_each_entry(phb, &hose_list, list_node) { | ||
317 | struct device_node *np = phb->dn; | ||
318 | const char *model = of_get_property(np, "model", NULL); | ||
319 | |||
320 | /* If no model property or name isn't exactly "pci", skip */ | ||
321 | if (model == NULL || strcmp(np->name, "pci")) | ||
322 | continue; | ||
323 | /* If model is not "Spider", skip */ | ||
324 | if (strcmp(model, "Spider")) | ||
325 | continue; | ||
326 | spider_pci_add_one(phb); | ||
327 | } | ||
328 | |||
329 | /* No Spider PCI found, exit */ | ||
330 | if (spider_pci_count == 0) | ||
331 | return 0; | ||
332 | 179 | ||
333 | /* Setup IO callbacks. We only setup MMIO reads. PIO reads will | 180 | if (io_workaround_inited) |
334 | * fallback to MMIO reads (though without a token, thus slower) | 181 | return; |
335 | */ | 182 | ppc_pci_io = iowa_pci_io; |
336 | ppc_pci_io = spider_pci_io; | 183 | ppc_md.ioremap = iowa_ioremap; |
337 | 184 | io_workaround_inited = 1; | |
338 | /* Setup ioremap callback */ | ||
339 | ppc_md.ioremap = spider_ioremap; | ||
340 | |||
341 | return 0; | ||
342 | } | 185 | } |
343 | machine_arch_initcall(cell, spider_pci_workaround_init); | ||
diff --git a/arch/powerpc/platforms/cell/io-workarounds.h b/arch/powerpc/platforms/cell/io-workarounds.h new file mode 100644 index 000000000000..79d8ed3d510f --- /dev/null +++ b/arch/powerpc/platforms/cell/io-workarounds.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * Support PCI IO workaround | ||
3 | * | ||
4 | * (C) Copyright 2007-2008 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef _IO_WORKAROUNDS_H | ||
22 | #define _IO_WORKAROUNDS_H | ||
23 | |||
24 | #include <linux/io.h> | ||
25 | #include <asm/pci-bridge.h> | ||
26 | |||
27 | /* Bus info */ | ||
28 | struct iowa_bus { | ||
29 | struct pci_controller *phb; | ||
30 | struct ppc_pci_io *ops; | ||
31 | void *private; | ||
32 | }; | ||
33 | |||
34 | void __init io_workaround_init(void); | ||
35 | void __init iowa_register_bus(struct pci_controller *, struct ppc_pci_io *, | ||
36 | int (*)(struct iowa_bus *, void *), void *); | ||
37 | struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR); | ||
38 | struct iowa_bus *iowa_pio_find_bus(unsigned long); | ||
39 | |||
40 | extern struct ppc_pci_io spiderpci_ops; | ||
41 | extern int spiderpci_iowa_init(struct iowa_bus *, void *); | ||
42 | |||
43 | #define SPIDER_PCI_REG_BASE 0xd000 | ||
44 | #define SPIDER_PCI_REG_SIZE 0x1000 | ||
45 | #define SPIDER_PCI_VCI_CNTL_STAT 0x0110 | ||
46 | #define SPIDER_PCI_DUMMY_READ 0x0810 | ||
47 | #define SPIDER_PCI_DUMMY_READ_BASE 0x0814 | ||
48 | |||
49 | #endif /* _IO_WORKAROUNDS_H */ | ||
diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index 5c531e8f9f6f..ab721b50fbba 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c | |||
@@ -57,6 +57,7 @@ | |||
57 | #include "interrupt.h" | 57 | #include "interrupt.h" |
58 | #include "pervasive.h" | 58 | #include "pervasive.h" |
59 | #include "ras.h" | 59 | #include "ras.h" |
60 | #include "io-workarounds.h" | ||
60 | 61 | ||
61 | #ifdef DEBUG | 62 | #ifdef DEBUG |
62 | #define DBG(fmt...) udbg_printf(fmt) | 63 | #define DBG(fmt...) udbg_printf(fmt) |
@@ -117,13 +118,50 @@ static void cell_fixup_pcie_rootcomplex(struct pci_dev *dev) | |||
117 | } | 118 | } |
118 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, cell_fixup_pcie_rootcomplex); | 119 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, cell_fixup_pcie_rootcomplex); |
119 | 120 | ||
121 | static int __devinit cell_setup_phb(struct pci_controller *phb) | ||
122 | { | ||
123 | const char *model; | ||
124 | struct device_node *np; | ||
125 | |||
126 | int rc = rtas_setup_phb(phb); | ||
127 | if (rc) | ||
128 | return rc; | ||
129 | |||
130 | np = phb->dn; | ||
131 | model = of_get_property(np, "model", NULL); | ||
132 | if (model == NULL || strcmp(np->name, "pci")) | ||
133 | return 0; | ||
134 | |||
135 | /* Setup workarounds for spider */ | ||
136 | if (strcmp(model, "Spider")) | ||
137 | return 0; | ||
138 | |||
139 | iowa_register_bus(phb, &spiderpci_ops, &spiderpci_iowa_init, | ||
140 | (void *)SPIDER_PCI_REG_BASE); | ||
141 | io_workaround_init(); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
120 | static int __init cell_publish_devices(void) | 146 | static int __init cell_publish_devices(void) |
121 | { | 147 | { |
148 | struct device_node *root = of_find_node_by_path("/"); | ||
149 | struct device_node *np; | ||
122 | int node; | 150 | int node; |
123 | 151 | ||
124 | /* Publish OF platform devices for southbridge IOs */ | 152 | /* Publish OF platform devices for southbridge IOs */ |
125 | of_platform_bus_probe(NULL, NULL, NULL); | 153 | of_platform_bus_probe(NULL, NULL, NULL); |
126 | 154 | ||
155 | /* On spider based blades, we need to manually create the OF | ||
156 | * platform devices for the PCI host bridges | ||
157 | */ | ||
158 | for_each_child_of_node(root, np) { | ||
159 | if (np->type == NULL || (strcmp(np->type, "pci") != 0 && | ||
160 | strcmp(np->type, "pciex") != 0)) | ||
161 | continue; | ||
162 | of_platform_device_create(np, NULL, NULL); | ||
163 | } | ||
164 | |||
127 | /* There is no device for the MIC memory controller, thus we create | 165 | /* There is no device for the MIC memory controller, thus we create |
128 | * a platform device for it to attach the EDAC driver to. | 166 | * a platform device for it to attach the EDAC driver to. |
129 | */ | 167 | */ |
@@ -132,6 +170,7 @@ static int __init cell_publish_devices(void) | |||
132 | continue; | 170 | continue; |
133 | platform_device_register_simple("cbe-mic", node, NULL, 0); | 171 | platform_device_register_simple("cbe-mic", node, NULL, 0); |
134 | } | 172 | } |
173 | |||
135 | return 0; | 174 | return 0; |
136 | } | 175 | } |
137 | machine_subsys_initcall(cell, cell_publish_devices); | 176 | machine_subsys_initcall(cell, cell_publish_devices); |
@@ -213,7 +252,7 @@ static void __init cell_setup_arch(void) | |||
213 | 252 | ||
214 | /* Find and initialize PCI host bridges */ | 253 | /* Find and initialize PCI host bridges */ |
215 | init_pci_config_tokens(); | 254 | init_pci_config_tokens(); |
216 | find_and_init_phbs(); | 255 | |
217 | cbe_pervasive_init(); | 256 | cbe_pervasive_init(); |
218 | #ifdef CONFIG_DUMMY_CONSOLE | 257 | #ifdef CONFIG_DUMMY_CONSOLE |
219 | conswitchp = &dummy_con; | 258 | conswitchp = &dummy_con; |
@@ -249,7 +288,7 @@ define_machine(cell) { | |||
249 | .calibrate_decr = generic_calibrate_decr, | 288 | .calibrate_decr = generic_calibrate_decr, |
250 | .progress = cell_progress, | 289 | .progress = cell_progress, |
251 | .init_IRQ = cell_init_irq, | 290 | .init_IRQ = cell_init_irq, |
252 | .pci_setup_phb = rtas_setup_phb, | 291 | .pci_setup_phb = cell_setup_phb, |
253 | #ifdef CONFIG_KEXEC | 292 | #ifdef CONFIG_KEXEC |
254 | .machine_kexec = default_machine_kexec, | 293 | .machine_kexec = default_machine_kexec, |
255 | .machine_kexec_prepare = default_machine_kexec_prepare, | 294 | .machine_kexec_prepare = default_machine_kexec_prepare, |
diff --git a/arch/powerpc/platforms/cell/spider-pci.c b/arch/powerpc/platforms/cell/spider-pci.c new file mode 100644 index 000000000000..418b605ac35a --- /dev/null +++ b/arch/powerpc/platforms/cell/spider-pci.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | * IO workarounds for PCI on Celleb/Cell platform | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #undef DEBUG | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/of_platform.h> | ||
25 | #include <linux/io.h> | ||
26 | |||
27 | #include <asm/ppc-pci.h> | ||
28 | #include <asm/pci-bridge.h> | ||
29 | |||
30 | #include "io-workarounds.h" | ||
31 | |||
32 | #define SPIDER_PCI_DISABLE_PREFETCH | ||
33 | |||
34 | struct spiderpci_iowa_private { | ||
35 | void __iomem *regs; | ||
36 | }; | ||
37 | |||
38 | static void spiderpci_io_flush(struct iowa_bus *bus) | ||
39 | { | ||
40 | struct spiderpci_iowa_private *priv; | ||
41 | u32 val; | ||
42 | |||
43 | priv = bus->private; | ||
44 | val = in_be32(priv->regs + SPIDER_PCI_DUMMY_READ); | ||
45 | iosync(); | ||
46 | } | ||
47 | |||
48 | #define SPIDER_PCI_MMIO_READ(name, ret) \ | ||
49 | static ret spiderpci_##name(const PCI_IO_ADDR addr) \ | ||
50 | { \ | ||
51 | ret val = __do_##name(addr); \ | ||
52 | spiderpci_io_flush(iowa_mem_find_bus(addr)); \ | ||
53 | return val; \ | ||
54 | } | ||
55 | |||
56 | #define SPIDER_PCI_MMIO_READ_STR(name) \ | ||
57 | static void spiderpci_##name(const PCI_IO_ADDR addr, void *buf, \ | ||
58 | unsigned long count) \ | ||
59 | { \ | ||
60 | __do_##name(addr, buf, count); \ | ||
61 | spiderpci_io_flush(iowa_mem_find_bus(addr)); \ | ||
62 | } | ||
63 | |||
64 | SPIDER_PCI_MMIO_READ(readb, u8) | ||
65 | SPIDER_PCI_MMIO_READ(readw, u16) | ||
66 | SPIDER_PCI_MMIO_READ(readl, u32) | ||
67 | SPIDER_PCI_MMIO_READ(readq, u64) | ||
68 | SPIDER_PCI_MMIO_READ(readw_be, u16) | ||
69 | SPIDER_PCI_MMIO_READ(readl_be, u32) | ||
70 | SPIDER_PCI_MMIO_READ(readq_be, u64) | ||
71 | SPIDER_PCI_MMIO_READ_STR(readsb) | ||
72 | SPIDER_PCI_MMIO_READ_STR(readsw) | ||
73 | SPIDER_PCI_MMIO_READ_STR(readsl) | ||
74 | |||
75 | static void spiderpci_memcpy_fromio(void *dest, const PCI_IO_ADDR src, | ||
76 | unsigned long n) | ||
77 | { | ||
78 | __do_memcpy_fromio(dest, src, n); | ||
79 | spiderpci_io_flush(iowa_mem_find_bus(src)); | ||
80 | } | ||
81 | |||
82 | static int __init spiderpci_pci_setup_chip(struct pci_controller *phb, | ||
83 | void __iomem *regs) | ||
84 | { | ||
85 | void *dummy_page_va; | ||
86 | dma_addr_t dummy_page_da; | ||
87 | |||
88 | #ifdef SPIDER_PCI_DISABLE_PREFETCH | ||
89 | u32 val = in_be32(regs + SPIDER_PCI_VCI_CNTL_STAT); | ||
90 | pr_debug("SPIDER_IOWA:PVCI_Control_Status was 0x%08x\n", val); | ||
91 | out_be32(regs + SPIDER_PCI_VCI_CNTL_STAT, val | 0x8); | ||
92 | #endif /* SPIDER_PCI_DISABLE_PREFETCH */ | ||
93 | |||
94 | /* setup dummy read */ | ||
95 | /* | ||
96 | * On CellBlade, we can't know that which XDR memory is used by | ||
97 | * kmalloc() to allocate dummy_page_va. | ||
98 | * In order to imporve the performance, the XDR which is used to | ||
99 | * allocate dummy_page_va is the nearest the spider-pci. | ||
100 | * We have to select the CBE which is the nearest the spider-pci | ||
101 | * to allocate memory from the best XDR, but I don't know that | ||
102 | * how to do. | ||
103 | * | ||
104 | * Celleb does not have this problem, because it has only one XDR. | ||
105 | */ | ||
106 | dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
107 | if (!dummy_page_va) { | ||
108 | pr_err("SPIDERPCI-IOWA:Alloc dummy_page_va failed.\n"); | ||
109 | return -1; | ||
110 | } | ||
111 | |||
112 | dummy_page_da = dma_map_single(phb->parent, dummy_page_va, | ||
113 | PAGE_SIZE, DMA_FROM_DEVICE); | ||
114 | if (dma_mapping_error(dummy_page_da)) { | ||
115 | pr_err("SPIDER-IOWA:Map dummy page filed.\n"); | ||
116 | kfree(dummy_page_va); | ||
117 | return -1; | ||
118 | } | ||
119 | |||
120 | out_be32(regs + SPIDER_PCI_DUMMY_READ_BASE, dummy_page_da); | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | int __init spiderpci_iowa_init(struct iowa_bus *bus, void *data) | ||
126 | { | ||
127 | void __iomem *regs = NULL; | ||
128 | struct spiderpci_iowa_private *priv; | ||
129 | struct device_node *np = bus->phb->dn; | ||
130 | struct resource r; | ||
131 | unsigned long offset = (unsigned long)data; | ||
132 | |||
133 | pr_debug("SPIDERPCI-IOWA:Bus initialize for spider(%s)\n", | ||
134 | np->full_name); | ||
135 | |||
136 | priv = kzalloc(sizeof(struct spiderpci_iowa_private), GFP_KERNEL); | ||
137 | if (!priv) { | ||
138 | pr_err("SPIDERPCI-IOWA:" | ||
139 | "Can't allocate struct spiderpci_iowa_private"); | ||
140 | return -1; | ||
141 | } | ||
142 | |||
143 | if (of_address_to_resource(np, 0, &r)) { | ||
144 | pr_err("SPIDERPCI-IOWA:Can't get resource.\n"); | ||
145 | goto error; | ||
146 | } | ||
147 | |||
148 | regs = ioremap(r.start + offset, SPIDER_PCI_REG_SIZE); | ||
149 | if (!regs) { | ||
150 | pr_err("SPIDERPCI-IOWA:ioremap failed.\n"); | ||
151 | goto error; | ||
152 | } | ||
153 | priv->regs = regs; | ||
154 | bus->private = priv; | ||
155 | |||
156 | if (spiderpci_pci_setup_chip(bus->phb, regs)) | ||
157 | goto error; | ||
158 | |||
159 | return 0; | ||
160 | |||
161 | error: | ||
162 | kfree(priv); | ||
163 | bus->private = NULL; | ||
164 | |||
165 | if (regs) | ||
166 | iounmap(regs); | ||
167 | |||
168 | return -1; | ||
169 | } | ||
170 | |||
171 | struct ppc_pci_io spiderpci_ops = { | ||
172 | .readb = spiderpci_readb, | ||
173 | .readw = spiderpci_readw, | ||
174 | .readl = spiderpci_readl, | ||
175 | .readq = spiderpci_readq, | ||
176 | .readw_be = spiderpci_readw_be, | ||
177 | .readl_be = spiderpci_readl_be, | ||
178 | .readq_be = spiderpci_readq_be, | ||
179 | .readsb = spiderpci_readsb, | ||
180 | .readsw = spiderpci_readsw, | ||
181 | .readsl = spiderpci_readsl, | ||
182 | .memcpy_fromio = spiderpci_memcpy_fromio, | ||
183 | }; | ||
184 | |||