diff options
author | Ishizaki Kou <kou.ishizaki@toshiba.co.jp> | 2007-10-02 04:26:53 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-10-02 23:25:28 -0400 |
commit | da0bd34e03b48f366a29e0ecdb24f8a0fe856d40 (patch) | |
tree | b2719a810e67826dbde6079ff73f091060f92ddf /arch | |
parent | 86de9f5f5e663123a5a49c6fe39750bea3dba24c (diff) |
[POWERPC] Celleb: update for PCI
This adds support for the PCI bus on Celleb with new "I/O routines
for PowerPC." External PCI on Celleb must do explicit synchronization
with devices (Bus has no automatic synchronization feature).
Signed-off-by: Kou Ishizaki <Kou.Ishizaki@toshiba.co.jp>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/platforms/celleb/Kconfig | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/celleb/Makefile | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/celleb/io-workarounds.c | 279 | ||||
-rw-r--r-- | arch/powerpc/platforms/celleb/pci.c | 65 | ||||
-rw-r--r-- | arch/powerpc/platforms/celleb/pci.h | 9 | ||||
-rw-r--r-- | arch/powerpc/platforms/celleb/scc.h | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/celleb/scc_epci.c | 80 | ||||
-rw-r--r-- | arch/powerpc/platforms/celleb/setup.c | 2 |
8 files changed, 396 insertions, 47 deletions
diff --git a/arch/powerpc/platforms/celleb/Kconfig b/arch/powerpc/platforms/celleb/Kconfig index 2db1e293433e..04748d410fc9 100644 --- a/arch/powerpc/platforms/celleb/Kconfig +++ b/arch/powerpc/platforms/celleb/Kconfig | |||
@@ -2,6 +2,7 @@ config PPC_CELLEB | |||
2 | bool "Toshiba's Cell Reference Set 'Celleb' Architecture" | 2 | bool "Toshiba's Cell Reference Set 'Celleb' Architecture" |
3 | depends on PPC_MULTIPLATFORM && PPC64 | 3 | depends on PPC_MULTIPLATFORM && PPC64 |
4 | select PPC_CELL | 4 | select PPC_CELL |
5 | select PPC_INDIRECT_IO | ||
5 | select PPC_OF_PLATFORM_PCI | 6 | select PPC_OF_PLATFORM_PCI |
6 | select HAS_TXX9_SERIAL | 7 | select HAS_TXX9_SERIAL |
7 | select PPC_UDBG_BEAT | 8 | select PPC_UDBG_BEAT |
diff --git a/arch/powerpc/platforms/celleb/Makefile b/arch/powerpc/platforms/celleb/Makefile index 5240046d8671..889d43f715ea 100644 --- a/arch/powerpc/platforms/celleb/Makefile +++ b/arch/powerpc/platforms/celleb/Makefile | |||
@@ -1,6 +1,7 @@ | |||
1 | obj-y += interrupt.o iommu.o setup.o \ | 1 | obj-y += interrupt.o iommu.o setup.o \ |
2 | htab.o beat.o pci.o \ | 2 | htab.o beat.o hvCall.o pci.o \ |
3 | scc_epci.o scc_uhc.o hvCall.o | 3 | scc_epci.o scc_uhc.o \ |
4 | io-workarounds.o | ||
4 | 5 | ||
5 | obj-$(CONFIG_SMP) += smp.o | 6 | obj-$(CONFIG_SMP) += smp.o |
6 | obj-$(CONFIG_PPC_UDBG_BEAT) += udbg_beat.o | 7 | obj-$(CONFIG_PPC_UDBG_BEAT) += udbg_beat.o |
diff --git a/arch/powerpc/platforms/celleb/io-workarounds.c b/arch/powerpc/platforms/celleb/io-workarounds.c new file mode 100644 index 000000000000..2b912140bcbb --- /dev/null +++ b/arch/powerpc/platforms/celleb/io-workarounds.c | |||
@@ -0,0 +1,279 @@ | |||
1 | /* | ||
2 | * Support for Celleb io workarounds | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This file is based to arch/powerpc/platform/cell/io-workarounds.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
21 | */ | ||
22 | |||
23 | #undef DEBUG | ||
24 | |||
25 | #include <linux/of_device.h> | ||
26 | #include <linux/irq.h> | ||
27 | |||
28 | #include <asm/io.h> | ||
29 | #include <asm/prom.h> | ||
30 | #include <asm/machdep.h> | ||
31 | #include <asm/pci-bridge.h> | ||
32 | #include <asm/ppc-pci.h> | ||
33 | |||
34 | #include "pci.h" | ||
35 | |||
36 | #define MAX_CELLEB_PCI_BUS 4 | ||
37 | |||
38 | void *celleb_dummy_page_va; | ||
39 | |||
40 | static struct celleb_pci_bus { | ||
41 | struct pci_controller *phb; | ||
42 | void (*dummy_read)(struct pci_controller *); | ||
43 | } celleb_pci_busses[MAX_CELLEB_PCI_BUS]; | ||
44 | |||
45 | static int celleb_pci_count = 0; | ||
46 | |||
47 | static struct celleb_pci_bus *celleb_pci_find(unsigned long vaddr, | ||
48 | unsigned long paddr) | ||
49 | { | ||
50 | int i, j; | ||
51 | struct resource *res; | ||
52 | |||
53 | for (i = 0; i < celleb_pci_count; i++) { | ||
54 | struct celleb_pci_bus *bus = &celleb_pci_busses[i]; | ||
55 | struct pci_controller *phb = bus->phb; | ||
56 | if (paddr) | ||
57 | for (j = 0; j < 3; j++) { | ||
58 | res = &phb->mem_resources[j]; | ||
59 | if (paddr >= res->start && paddr <= res->end) | ||
60 | return bus; | ||
61 | } | ||
62 | res = &phb->io_resource; | ||
63 | if (vaddr && vaddr >= res->start && vaddr <= res->end) | ||
64 | return bus; | ||
65 | } | ||
66 | return NULL; | ||
67 | } | ||
68 | |||
69 | static void celleb_io_flush(const PCI_IO_ADDR addr) | ||
70 | { | ||
71 | struct celleb_pci_bus *bus; | ||
72 | int token; | ||
73 | |||
74 | token = PCI_GET_ADDR_TOKEN(addr); | ||
75 | |||
76 | if (token && token <= celleb_pci_count) | ||
77 | bus = &celleb_pci_busses[token - 1]; | ||
78 | else { | ||
79 | unsigned long vaddr, paddr; | ||
80 | pte_t *ptep; | ||
81 | |||
82 | vaddr = (unsigned long)PCI_FIX_ADDR(addr); | ||
83 | if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END) | ||
84 | return; | ||
85 | |||
86 | ptep = find_linux_pte(init_mm.pgd, vaddr); | ||
87 | if (ptep == NULL) | ||
88 | paddr = 0; | ||
89 | else | ||
90 | paddr = pte_pfn(*ptep) << PAGE_SHIFT; | ||
91 | bus = celleb_pci_find(vaddr, paddr); | ||
92 | |||
93 | if (bus == NULL) | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | if (bus->dummy_read) | ||
98 | bus->dummy_read(bus->phb); | ||
99 | } | ||
100 | |||
101 | static u8 celleb_readb(const PCI_IO_ADDR addr) | ||
102 | { | ||
103 | u8 val; | ||
104 | val = __do_readb(addr); | ||
105 | celleb_io_flush(addr); | ||
106 | return val; | ||
107 | } | ||
108 | |||
109 | static u16 celleb_readw(const PCI_IO_ADDR addr) | ||
110 | { | ||
111 | u16 val; | ||
112 | val = __do_readw(addr); | ||
113 | celleb_io_flush(addr); | ||
114 | return val; | ||
115 | } | ||
116 | |||
117 | static u32 celleb_readl(const PCI_IO_ADDR addr) | ||
118 | { | ||
119 | u32 val; | ||
120 | val = __do_readl(addr); | ||
121 | celleb_io_flush(addr); | ||
122 | return val; | ||
123 | } | ||
124 | |||
125 | static u64 celleb_readq(const PCI_IO_ADDR addr) | ||
126 | { | ||
127 | u64 val; | ||
128 | val = __do_readq(addr); | ||
129 | celleb_io_flush(addr); | ||
130 | return val; | ||
131 | } | ||
132 | |||
133 | static u16 celleb_readw_be(const PCI_IO_ADDR addr) | ||
134 | { | ||
135 | u16 val; | ||
136 | val = __do_readw_be(addr); | ||
137 | celleb_io_flush(addr); | ||
138 | return val; | ||
139 | } | ||
140 | |||
141 | static u32 celleb_readl_be(const PCI_IO_ADDR addr) | ||
142 | { | ||
143 | u32 val; | ||
144 | val = __do_readl_be(addr); | ||
145 | celleb_io_flush(addr); | ||
146 | return val; | ||
147 | } | ||
148 | |||
149 | static u64 celleb_readq_be(const PCI_IO_ADDR addr) | ||
150 | { | ||
151 | u64 val; | ||
152 | val = __do_readq_be(addr); | ||
153 | celleb_io_flush(addr); | ||
154 | return val; | ||
155 | } | ||
156 | |||
157 | static void celleb_readsb(const PCI_IO_ADDR addr, | ||
158 | void *buf, unsigned long count) | ||
159 | { | ||
160 | __do_readsb(addr, buf, count); | ||
161 | celleb_io_flush(addr); | ||
162 | } | ||
163 | |||
164 | static void celleb_readsw(const PCI_IO_ADDR addr, | ||
165 | void *buf, unsigned long count) | ||
166 | { | ||
167 | __do_readsw(addr, buf, count); | ||
168 | celleb_io_flush(addr); | ||
169 | } | ||
170 | |||
171 | static void celleb_readsl(const PCI_IO_ADDR addr, | ||
172 | void *buf, unsigned long count) | ||
173 | { | ||
174 | __do_readsl(addr, buf, count); | ||
175 | celleb_io_flush(addr); | ||
176 | } | ||
177 | |||
178 | static void celleb_memcpy_fromio(void *dest, | ||
179 | const PCI_IO_ADDR src, | ||
180 | unsigned long n) | ||
181 | { | ||
182 | __do_memcpy_fromio(dest, src, n); | ||
183 | celleb_io_flush(src); | ||
184 | } | ||
185 | |||
186 | static void __iomem *celleb_ioremap(unsigned long addr, | ||
187 | unsigned long size, | ||
188 | unsigned long flags) | ||
189 | { | ||
190 | struct celleb_pci_bus *bus; | ||
191 | void __iomem *res = __ioremap(addr, size, flags); | ||
192 | int busno; | ||
193 | |||
194 | bus = celleb_pci_find(0, addr); | ||
195 | if (bus != NULL) { | ||
196 | busno = bus - celleb_pci_busses; | ||
197 | PCI_SET_ADDR_TOKEN(res, busno + 1); | ||
198 | } | ||
199 | return res; | ||
200 | } | ||
201 | |||
202 | static void celleb_iounmap(volatile void __iomem *addr) | ||
203 | { | ||
204 | return __iounmap(PCI_FIX_ADDR(addr)); | ||
205 | } | ||
206 | |||
207 | static struct ppc_pci_io celleb_pci_io __initdata = { | ||
208 | .readb = celleb_readb, | ||
209 | .readw = celleb_readw, | ||
210 | .readl = celleb_readl, | ||
211 | .readq = celleb_readq, | ||
212 | .readw_be = celleb_readw_be, | ||
213 | .readl_be = celleb_readl_be, | ||
214 | .readq_be = celleb_readq_be, | ||
215 | .readsb = celleb_readsb, | ||
216 | .readsw = celleb_readsw, | ||
217 | .readsl = celleb_readsl, | ||
218 | .memcpy_fromio = celleb_memcpy_fromio, | ||
219 | }; | ||
220 | |||
221 | void __init celleb_pci_add_one(struct pci_controller *phb, | ||
222 | void (*dummy_read)(struct pci_controller *)) | ||
223 | { | ||
224 | struct celleb_pci_bus *bus = &celleb_pci_busses[celleb_pci_count]; | ||
225 | struct device_node *np = phb->arch_data; | ||
226 | |||
227 | if (celleb_pci_count >= MAX_CELLEB_PCI_BUS) { | ||
228 | printk(KERN_ERR "Too many pci bridges, workarounds" | ||
229 | " disabled for %s\n", np->full_name); | ||
230 | return; | ||
231 | } | ||
232 | |||
233 | celleb_pci_count++; | ||
234 | |||
235 | bus->phb = phb; | ||
236 | bus->dummy_read = dummy_read; | ||
237 | } | ||
238 | |||
239 | static struct of_device_id celleb_pci_workaround_match[] __initdata = { | ||
240 | { | ||
241 | .name = "pci-pseudo", | ||
242 | .data = fake_pci_workaround_init, | ||
243 | }, { | ||
244 | .name = "epci", | ||
245 | .data = epci_workaround_init, | ||
246 | }, { | ||
247 | }, | ||
248 | }; | ||
249 | |||
250 | int __init celleb_pci_workaround_init(void) | ||
251 | { | ||
252 | struct pci_controller *phb; | ||
253 | struct device_node *node; | ||
254 | const struct of_device_id *match; | ||
255 | void (*init_func)(struct pci_controller *); | ||
256 | |||
257 | celleb_dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
258 | if (!celleb_dummy_page_va) { | ||
259 | printk(KERN_ERR "Celleb: dummy read disabled." | ||
260 | "Alloc celleb_dummy_page_va failed\n"); | ||
261 | return 1; | ||
262 | } | ||
263 | |||
264 | list_for_each_entry(phb, &hose_list, list_node) { | ||
265 | node = phb->arch_data; | ||
266 | match = of_match_node(celleb_pci_workaround_match, node); | ||
267 | |||
268 | if (match) { | ||
269 | init_func = match->data; | ||
270 | (*init_func)(phb); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | ppc_pci_io = celleb_pci_io; | ||
275 | ppc_md.ioremap = celleb_ioremap; | ||
276 | ppc_md.iounmap = celleb_iounmap; | ||
277 | |||
278 | return 0; | ||
279 | } | ||
diff --git a/arch/powerpc/platforms/celleb/pci.c b/arch/powerpc/platforms/celleb/pci.c index 1348b23cbbc9..6bc32fda7a6b 100644 --- a/arch/powerpc/platforms/celleb/pci.c +++ b/arch/powerpc/platforms/celleb/pci.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/init.h> | 31 | #include <linux/init.h> |
32 | #include <linux/bootmem.h> | 32 | #include <linux/bootmem.h> |
33 | #include <linux/pci_regs.h> | 33 | #include <linux/pci_regs.h> |
34 | #include <linux/of_device.h> | ||
34 | 35 | ||
35 | #include <asm/io.h> | 36 | #include <asm/io.h> |
36 | #include <asm/irq.h> | 37 | #include <asm/irq.h> |
@@ -435,36 +436,58 @@ static void __init celleb_alloc_private_mem(struct pci_controller *hose) | |||
435 | GFP_KERNEL); | 436 | GFP_KERNEL); |
436 | } | 437 | } |
437 | 438 | ||
438 | int __init celleb_setup_phb(struct pci_controller *phb) | 439 | static int __init celleb_setup_fake_pci(struct device_node *dev, |
440 | struct pci_controller *phb) | ||
439 | { | 441 | { |
440 | const char *name; | ||
441 | struct device_node *dev = phb->arch_data; | ||
442 | struct device_node *node; | 442 | struct device_node *node; |
443 | unsigned int rlen; | ||
444 | 443 | ||
445 | name = of_get_property(dev, "name", &rlen); | 444 | phb->ops = &celleb_fake_pci_ops; |
446 | if (!name) | 445 | celleb_alloc_private_mem(phb); |
447 | return 1; | ||
448 | 446 | ||
449 | pr_debug("PCI: celleb_setup_phb() %s\n", name); | 447 | for (node = of_get_next_child(dev, NULL); |
450 | phb_set_bus_ranges(dev, phb); | 448 | node != NULL; node = of_get_next_child(dev, node)) |
451 | phb->buid = 1; | 449 | celleb_setup_fake_pci_device(node, phb); |
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | void __init fake_pci_workaround_init(struct pci_controller *phb) | ||
455 | { | ||
456 | /** | ||
457 | * We will add fake pci bus to scc_pci_bus for the purpose to improve | ||
458 | * I/O Macro performance. But device-tree and device drivers | ||
459 | * are not ready to use address with a token. | ||
460 | */ | ||
452 | 461 | ||
453 | if (strcmp(name, "epci") == 0) { | 462 | /* celleb_pci_add_one(phb, NULL); */ |
454 | phb->ops = &celleb_epci_ops; | 463 | } |
455 | return celleb_setup_epci(dev, phb); | ||
456 | 464 | ||
457 | } else if (strcmp(name, "pci-pseudo") == 0) { | 465 | static struct of_device_id celleb_phb_match[] __initdata = { |
458 | phb->ops = &celleb_fake_pci_ops; | 466 | { |
459 | celleb_alloc_private_mem(phb); | 467 | .name = "pci-pseudo", |
460 | for (node = of_get_next_child(dev, NULL); | 468 | .data = celleb_setup_fake_pci, |
461 | node != NULL; node = of_get_next_child(dev, node)) | 469 | }, { |
462 | celleb_setup_fake_pci_device(node, phb); | 470 | .name = "epci", |
471 | .data = celleb_setup_epci, | ||
472 | }, { | ||
473 | }, | ||
474 | }; | ||
463 | 475 | ||
464 | } else | 476 | int __init celleb_setup_phb(struct pci_controller *phb) |
477 | { | ||
478 | struct device_node *dev = phb->arch_data; | ||
479 | const struct of_device_id *match; | ||
480 | int (*setup_func)(struct device_node *, struct pci_controller *); | ||
481 | |||
482 | match = of_match_node(celleb_phb_match, dev); | ||
483 | if (!match) | ||
465 | return 1; | 484 | return 1; |
466 | 485 | ||
467 | return 0; | 486 | phb_set_bus_ranges(dev, phb); |
487 | phb->buid = 1; | ||
488 | |||
489 | setup_func = match->data; | ||
490 | return (*setup_func)(dev, phb); | ||
468 | } | 491 | } |
469 | 492 | ||
470 | int celleb_pci_probe_mode(struct pci_bus *bus) | 493 | int celleb_pci_probe_mode(struct pci_bus *bus) |
diff --git a/arch/powerpc/platforms/celleb/pci.h b/arch/powerpc/platforms/celleb/pci.h index 5340e348e297..5d5544ffeddb 100644 --- a/arch/powerpc/platforms/celleb/pci.h +++ b/arch/powerpc/platforms/celleb/pci.h | |||
@@ -25,11 +25,18 @@ | |||
25 | 25 | ||
26 | #include <asm/pci-bridge.h> | 26 | #include <asm/pci-bridge.h> |
27 | #include <asm/prom.h> | 27 | #include <asm/prom.h> |
28 | #include <asm/ppc-pci.h> | ||
28 | 29 | ||
29 | extern int celleb_setup_phb(struct pci_controller *); | 30 | extern int celleb_setup_phb(struct pci_controller *); |
30 | extern int celleb_pci_probe_mode(struct pci_bus *); | 31 | extern int celleb_pci_probe_mode(struct pci_bus *); |
31 | 32 | ||
32 | extern struct pci_ops celleb_epci_ops; | ||
33 | extern int celleb_setup_epci(struct device_node *, struct pci_controller *); | 33 | extern int celleb_setup_epci(struct device_node *, struct pci_controller *); |
34 | 34 | ||
35 | extern void *celleb_dummy_page_va; | ||
36 | extern int __init celleb_pci_workaround_init(void); | ||
37 | extern void __init celleb_pci_add_one(struct pci_controller *, | ||
38 | void (*)(struct pci_controller *)); | ||
39 | extern void fake_pci_workaround_init(struct pci_controller *); | ||
40 | extern void epci_workaround_init(struct pci_controller *); | ||
41 | |||
35 | #endif /* _CELLEB_PCI_H */ | 42 | #endif /* _CELLEB_PCI_H */ |
diff --git a/arch/powerpc/platforms/celleb/scc.h b/arch/powerpc/platforms/celleb/scc.h index e9ce8a7c1882..6be1542a6e66 100644 --- a/arch/powerpc/platforms/celleb/scc.h +++ b/arch/powerpc/platforms/celleb/scc.h | |||
@@ -53,7 +53,7 @@ | |||
53 | #define SCC_EPCI_STATUS 0x808 | 53 | #define SCC_EPCI_STATUS 0x808 |
54 | #define SCC_EPCI_ABTSET 0x80c | 54 | #define SCC_EPCI_ABTSET 0x80c |
55 | #define SCC_EPCI_WATRP 0x810 | 55 | #define SCC_EPCI_WATRP 0x810 |
56 | #define SCC_EPCI_DUMMYRADR 0x814 | 56 | #define SCC_EPCI_DUMYRADR 0x814 |
57 | #define SCC_EPCI_SWRESP 0x818 | 57 | #define SCC_EPCI_SWRESP 0x818 |
58 | #define SCC_EPCI_CNTOPT 0x81c | 58 | #define SCC_EPCI_CNTOPT 0x81c |
59 | #define SCC_EPCI_ECMODE 0xf00 | 59 | #define SCC_EPCI_ECMODE 0xf00 |
diff --git a/arch/powerpc/platforms/celleb/scc_epci.c b/arch/powerpc/platforms/celleb/scc_epci.c index 506fc8447554..9d076426676c 100644 --- a/arch/powerpc/platforms/celleb/scc_epci.c +++ b/arch/powerpc/platforms/celleb/scc_epci.c | |||
@@ -43,7 +43,11 @@ | |||
43 | 43 | ||
44 | #define iob() __asm__ __volatile__("eieio; sync":::"memory") | 44 | #define iob() __asm__ __volatile__("eieio; sync":::"memory") |
45 | 45 | ||
46 | static inline volatile void __iomem *celleb_epci_get_epci_base( | 46 | struct epci_private { |
47 | dma_addr_t dummy_page_da; | ||
48 | }; | ||
49 | |||
50 | static inline PCI_IO_ADDR celleb_epci_get_epci_base( | ||
47 | struct pci_controller *hose) | 51 | struct pci_controller *hose) |
48 | { | 52 | { |
49 | /* | 53 | /* |
@@ -55,7 +59,7 @@ static inline volatile void __iomem *celleb_epci_get_epci_base( | |||
55 | return hose->cfg_addr; | 59 | return hose->cfg_addr; |
56 | } | 60 | } |
57 | 61 | ||
58 | static inline volatile void __iomem *celleb_epci_get_epci_cfg( | 62 | static inline PCI_IO_ADDR celleb_epci_get_epci_cfg( |
59 | struct pci_controller *hose) | 63 | struct pci_controller *hose) |
60 | { | 64 | { |
61 | /* | 65 | /* |
@@ -67,20 +71,11 @@ static inline volatile void __iomem *celleb_epci_get_epci_cfg( | |||
67 | return hose->cfg_data; | 71 | return hose->cfg_data; |
68 | } | 72 | } |
69 | 73 | ||
70 | #if 0 /* test code for epci dummy read */ | 74 | static void scc_epci_dummy_read(struct pci_controller *hose) |
71 | static void celleb_epci_dummy_read(struct pci_dev *dev) | ||
72 | { | 75 | { |
73 | volatile void __iomem *epci_base; | 76 | PCI_IO_ADDR epci_base; |
74 | struct device_node *node; | ||
75 | struct pci_controller *hose; | ||
76 | u32 val; | 77 | u32 val; |
77 | 78 | ||
78 | node = (struct device_node *)dev->bus->sysdata; | ||
79 | hose = pci_find_hose_for_OF_device(node); | ||
80 | |||
81 | if (!hose) | ||
82 | return; | ||
83 | |||
84 | epci_base = celleb_epci_get_epci_base(hose); | 79 | epci_base = celleb_epci_get_epci_base(hose); |
85 | 80 | ||
86 | val = in_be32(epci_base + SCC_EPCI_WATRP); | 81 | val = in_be32(epci_base + SCC_EPCI_WATRP); |
@@ -88,21 +83,45 @@ static void celleb_epci_dummy_read(struct pci_dev *dev) | |||
88 | 83 | ||
89 | return; | 84 | return; |
90 | } | 85 | } |
91 | #endif | 86 | |
87 | void __init epci_workaround_init(struct pci_controller *hose) | ||
88 | { | ||
89 | PCI_IO_ADDR epci_base; | ||
90 | PCI_IO_ADDR reg; | ||
91 | struct epci_private *private = hose->private_data; | ||
92 | |||
93 | BUG_ON(!private); | ||
94 | |||
95 | private->dummy_page_da = dma_map_single(hose->parent, | ||
96 | celleb_dummy_page_va, PAGE_SIZE, DMA_FROM_DEVICE); | ||
97 | if (private->dummy_page_da == DMA_ERROR_CODE) { | ||
98 | printk(KERN_ERR "EPCI: dummy read disabled." | ||
99 | "Map dummy page failed.\n"); | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | celleb_pci_add_one(hose, scc_epci_dummy_read); | ||
104 | epci_base = celleb_epci_get_epci_base(hose); | ||
105 | |||
106 | reg = epci_base + SCC_EPCI_DUMYRADR; | ||
107 | out_be32(reg, private->dummy_page_da); | ||
108 | } | ||
92 | 109 | ||
93 | static inline void clear_and_disable_master_abort_interrupt( | 110 | static inline void clear_and_disable_master_abort_interrupt( |
94 | struct pci_controller *hose) | 111 | struct pci_controller *hose) |
95 | { | 112 | { |
96 | volatile void __iomem *epci_base, *reg; | 113 | PCI_IO_ADDR epci_base; |
114 | PCI_IO_ADDR reg; | ||
97 | epci_base = celleb_epci_get_epci_base(hose); | 115 | epci_base = celleb_epci_get_epci_base(hose); |
98 | reg = epci_base + PCI_COMMAND; | 116 | reg = epci_base + PCI_COMMAND; |
99 | out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16)); | 117 | out_be32(reg, in_be32(reg) | (PCI_STATUS_REC_MASTER_ABORT << 16)); |
100 | } | 118 | } |
101 | 119 | ||
102 | static int celleb_epci_check_abort(struct pci_controller *hose, | 120 | static int celleb_epci_check_abort(struct pci_controller *hose, |
103 | volatile void __iomem *addr) | 121 | PCI_IO_ADDR addr) |
104 | { | 122 | { |
105 | volatile void __iomem *reg, *epci_base; | 123 | PCI_IO_ADDR reg; |
124 | PCI_IO_ADDR epci_base; | ||
106 | u32 val; | 125 | u32 val; |
107 | 126 | ||
108 | iob(); | 127 | iob(); |
@@ -132,12 +151,12 @@ static int celleb_epci_check_abort(struct pci_controller *hose, | |||
132 | return PCIBIOS_SUCCESSFUL; | 151 | return PCIBIOS_SUCCESSFUL; |
133 | } | 152 | } |
134 | 153 | ||
135 | static volatile void __iomem *celleb_epci_make_config_addr( | 154 | static PCI_IO_ADDR celleb_epci_make_config_addr( |
136 | struct pci_bus *bus, | 155 | struct pci_bus *bus, |
137 | struct pci_controller *hose, | 156 | struct pci_controller *hose, |
138 | unsigned int devfn, int where) | 157 | unsigned int devfn, int where) |
139 | { | 158 | { |
140 | volatile void __iomem *addr; | 159 | PCI_IO_ADDR addr; |
141 | 160 | ||
142 | if (bus != hose->bus) | 161 | if (bus != hose->bus) |
143 | addr = celleb_epci_get_epci_cfg(hose) + | 162 | addr = celleb_epci_get_epci_cfg(hose) + |
@@ -157,7 +176,8 @@ static volatile void __iomem *celleb_epci_make_config_addr( | |||
157 | static int celleb_epci_read_config(struct pci_bus *bus, | 176 | static int celleb_epci_read_config(struct pci_bus *bus, |
158 | unsigned int devfn, int where, int size, u32 * val) | 177 | unsigned int devfn, int where, int size, u32 * val) |
159 | { | 178 | { |
160 | volatile void __iomem *epci_base, *addr; | 179 | PCI_IO_ADDR epci_base; |
180 | PCI_IO_ADDR addr; | ||
161 | struct device_node *node; | 181 | struct device_node *node; |
162 | struct pci_controller *hose; | 182 | struct pci_controller *hose; |
163 | 183 | ||
@@ -220,7 +240,8 @@ static int celleb_epci_read_config(struct pci_bus *bus, | |||
220 | static int celleb_epci_write_config(struct pci_bus *bus, | 240 | static int celleb_epci_write_config(struct pci_bus *bus, |
221 | unsigned int devfn, int where, int size, u32 val) | 241 | unsigned int devfn, int where, int size, u32 val) |
222 | { | 242 | { |
223 | volatile void __iomem *epci_base, *addr; | 243 | PCI_IO_ADDR epci_base; |
244 | PCI_IO_ADDR addr; | ||
224 | struct device_node *node; | 245 | struct device_node *node; |
225 | struct pci_controller *hose; | 246 | struct pci_controller *hose; |
226 | 247 | ||
@@ -286,7 +307,8 @@ struct pci_ops celleb_epci_ops = { | |||
286 | static int __init celleb_epci_init(struct pci_controller *hose) | 307 | static int __init celleb_epci_init(struct pci_controller *hose) |
287 | { | 308 | { |
288 | u32 val; | 309 | u32 val; |
289 | volatile void __iomem *reg, *epci_base; | 310 | PCI_IO_ADDR reg; |
311 | PCI_IO_ADDR epci_base; | ||
290 | int hwres = 0; | 312 | int hwres = 0; |
291 | 313 | ||
292 | epci_base = celleb_epci_get_epci_base(hose); | 314 | epci_base = celleb_epci_get_epci_base(hose); |
@@ -440,10 +462,24 @@ int __init celleb_setup_epci(struct device_node *node, | |||
440 | r.start, (unsigned long)hose->cfg_data, | 462 | r.start, (unsigned long)hose->cfg_data, |
441 | (r.end - r.start + 1)); | 463 | (r.end - r.start + 1)); |
442 | 464 | ||
465 | hose->private_data = kzalloc(sizeof(struct epci_private), GFP_KERNEL); | ||
466 | if (hose->private_data == NULL) { | ||
467 | printk(KERN_ERR "EPCI: no memory for private data.\n"); | ||
468 | goto error; | ||
469 | } | ||
470 | |||
471 | hose->ops = &celleb_epci_ops; | ||
443 | celleb_epci_init(hose); | 472 | celleb_epci_init(hose); |
444 | 473 | ||
445 | return 0; | 474 | return 0; |
446 | 475 | ||
447 | error: | 476 | error: |
477 | kfree(hose->private_data); | ||
478 | |||
479 | if (hose->cfg_addr) | ||
480 | iounmap(hose->cfg_addr); | ||
481 | |||
482 | if (hose->cfg_data) | ||
483 | iounmap(hose->cfg_data); | ||
448 | return 1; | 484 | return 1; |
449 | } | 485 | } |
diff --git a/arch/powerpc/platforms/celleb/setup.c b/arch/powerpc/platforms/celleb/setup.c index 59731e836e28..0f1dddb81cd2 100644 --- a/arch/powerpc/platforms/celleb/setup.c +++ b/arch/powerpc/platforms/celleb/setup.c | |||
@@ -137,6 +137,8 @@ static int __init celleb_publish_devices(void) | |||
137 | /* Publish OF platform devices for southbridge IOs */ | 137 | /* Publish OF platform devices for southbridge IOs */ |
138 | of_platform_bus_probe(NULL, celleb_bus_ids, NULL); | 138 | of_platform_bus_probe(NULL, celleb_bus_ids, NULL); |
139 | 139 | ||
140 | celleb_pci_workaround_init(); | ||
141 | |||
140 | return 0; | 142 | return 0; |
141 | } | 143 | } |
142 | device_initcall(celleb_publish_devices); | 144 | device_initcall(celleb_publish_devices); |