diff options
author | Arnd Bergmann <arnd@arndb.de> | 2005-06-22 19:43:23 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2005-06-22 19:43:23 -0400 |
commit | c5a3c2e52af1bcb118022ffac9a0fd1d42d43bd3 (patch) | |
tree | e9a646214342f6cd0f003a1677ae57f4dc9be012 /arch/ppc64/kernel | |
parent | 773bf9c469c01f01280c9bd45ec2462dd94d08a0 (diff) |
[PATCH] ppc64: Split out generic rtas code from pSeries_pci.c.
BPA is using rtas for PCI but should not be confused by
pSeries code. This also avoids some #ifdefs. Other
platforms that want to use rtas_pci.c could create
their own platform_pci.c with platform specific fixups.
Signed-off-by: Arnd Bergmann <arndb@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/ppc64/kernel')
-rw-r--r-- | arch/ppc64/kernel/Makefile | 3 | ||||
-rw-r--r-- | arch/ppc64/kernel/mpic.h | 3 | ||||
-rw-r--r-- | arch/ppc64/kernel/pSeries_pci.c | 476 | ||||
-rw-r--r-- | arch/ppc64/kernel/rtas_pci.c | 495 |
4 files changed, 506 insertions, 471 deletions
diff --git a/arch/ppc64/kernel/Makefile b/arch/ppc64/kernel/Makefile index b5e167cf1a05..f389f2453daa 100644 --- a/arch/ppc64/kernel/Makefile +++ b/arch/ppc64/kernel/Makefile | |||
@@ -31,13 +31,14 @@ obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o mpic.o | |||
31 | 31 | ||
32 | obj-$(CONFIG_PPC_PSERIES) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o \ | 32 | obj-$(CONFIG_PPC_PSERIES) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o \ |
33 | pSeries_nvram.o rtasd.o ras.o pSeries_reconfig.o \ | 33 | pSeries_nvram.o rtasd.o ras.o pSeries_reconfig.o \ |
34 | xics.o rtas.o pSeries_setup.o pSeries_iommu.o | 34 | xics.o pSeries_setup.o pSeries_iommu.o |
35 | 35 | ||
36 | obj-$(CONFIG_EEH) += eeh.o | 36 | obj-$(CONFIG_EEH) += eeh.o |
37 | obj-$(CONFIG_PROC_FS) += proc_ppc64.o | 37 | obj-$(CONFIG_PROC_FS) += proc_ppc64.o |
38 | obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o | 38 | obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o |
39 | obj-$(CONFIG_SMP) += smp.o | 39 | obj-$(CONFIG_SMP) += smp.o |
40 | obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o | 40 | obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o |
41 | obj-$(CONFIG_PPC_RTAS) += rtas.o rtas_pci.o | ||
41 | obj-$(CONFIG_RTAS_PROC) += rtas-proc.o | 42 | obj-$(CONFIG_RTAS_PROC) += rtas-proc.o |
42 | obj-$(CONFIG_SCANLOG) += scanlog.o | 43 | obj-$(CONFIG_SCANLOG) += scanlog.o |
43 | obj-$(CONFIG_VIOPATH) += viopath.o | 44 | obj-$(CONFIG_VIOPATH) += viopath.o |
diff --git a/arch/ppc64/kernel/mpic.h b/arch/ppc64/kernel/mpic.h index 571b3c99e062..63e177143eac 100644 --- a/arch/ppc64/kernel/mpic.h +++ b/arch/ppc64/kernel/mpic.h | |||
@@ -265,3 +265,6 @@ extern void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask); | |||
265 | extern int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs); | 265 | extern int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs); |
266 | /* This one gets to the primary mpic */ | 266 | /* This one gets to the primary mpic */ |
267 | extern int mpic_get_irq(struct pt_regs *regs); | 267 | extern int mpic_get_irq(struct pt_regs *regs); |
268 | |||
269 | /* global mpic for pSeries */ | ||
270 | extern struct mpic *pSeries_mpic; | ||
diff --git a/arch/ppc64/kernel/pSeries_pci.c b/arch/ppc64/kernel/pSeries_pci.c index 0b1cca281408..dfa6d3d3e9f0 100644 --- a/arch/ppc64/kernel/pSeries_pci.c +++ b/arch/ppc64/kernel/pSeries_pci.c | |||
@@ -1,13 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | * pSeries_pci.c | 2 | * arch/ppc64/kernel/pSeries_pci.c |
3 | * | 3 | * |
4 | * Copyright (C) 2001 Dave Engebretsen, IBM Corporation | 4 | * Copyright (C) 2001 Dave Engebretsen, IBM Corporation |
5 | * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM | 5 | * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM |
6 | * | 6 | * |
7 | * pSeries specific routines for PCI. | 7 | * pSeries specific routines for PCI. |
8 | * | 8 | * |
9 | * Based on code from pci.c and chrp_pci.c | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
12 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
13 | * the Free Software Foundation; either version 2 of the License, or | 11 | * the Free Software Foundation; either version 2 of the License, or |
@@ -23,430 +21,18 @@ | |||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 | */ | 22 | */ |
25 | 23 | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/ioport.h> | ||
26 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
27 | #include <linux/threads.h> | ||
28 | #include <linux/pci.h> | 27 | #include <linux/pci.h> |
29 | #include <linux/string.h> | 28 | #include <linux/string.h> |
30 | #include <linux/init.h> | ||
31 | #include <linux/bootmem.h> | ||
32 | 29 | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | #include <asm/irq.h> | ||
36 | #include <asm/prom.h> | ||
37 | #include <asm/machdep.h> | ||
38 | #include <asm/pci-bridge.h> | 30 | #include <asm/pci-bridge.h> |
39 | #include <asm/iommu.h> | 31 | #include <asm/prom.h> |
40 | #include <asm/rtas.h> | ||
41 | 32 | ||
42 | #include "mpic.h" | ||
43 | #include "pci.h" | 33 | #include "pci.h" |
44 | 34 | ||
45 | /* RTAS tokens */ | 35 | static int __initdata s7a_workaround; |
46 | static int read_pci_config; | ||
47 | static int write_pci_config; | ||
48 | static int ibm_read_pci_config; | ||
49 | static int ibm_write_pci_config; | ||
50 | |||
51 | static int s7a_workaround; | ||
52 | |||
53 | extern struct mpic *pSeries_mpic; | ||
54 | |||
55 | static int config_access_valid(struct device_node *dn, int where) | ||
56 | { | ||
57 | if (where < 256) | ||
58 | return 1; | ||
59 | if (where < 4096 && dn->pci_ext_config_space) | ||
60 | return 1; | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val) | ||
66 | { | ||
67 | int returnval = -1; | ||
68 | unsigned long buid, addr; | ||
69 | int ret; | ||
70 | |||
71 | if (!dn) | ||
72 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
73 | if (!config_access_valid(dn, where)) | ||
74 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
75 | |||
76 | addr = ((where & 0xf00) << 20) | (dn->busno << 16) | | ||
77 | (dn->devfn << 8) | (where & 0xff); | ||
78 | buid = dn->phb->buid; | ||
79 | if (buid) { | ||
80 | ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, | ||
81 | addr, buid >> 32, buid & 0xffffffff, size); | ||
82 | } else { | ||
83 | ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size); | ||
84 | } | ||
85 | *val = returnval; | ||
86 | |||
87 | if (ret) | ||
88 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
89 | |||
90 | if (returnval == EEH_IO_ERROR_VALUE(size) | ||
91 | && eeh_dn_check_failure (dn, NULL)) | ||
92 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
93 | |||
94 | return PCIBIOS_SUCCESSFUL; | ||
95 | } | ||
96 | |||
97 | static int rtas_pci_read_config(struct pci_bus *bus, | ||
98 | unsigned int devfn, | ||
99 | int where, int size, u32 *val) | ||
100 | { | ||
101 | struct device_node *busdn, *dn; | ||
102 | |||
103 | if (bus->self) | ||
104 | busdn = pci_device_to_OF_node(bus->self); | ||
105 | else | ||
106 | busdn = bus->sysdata; /* must be a phb */ | ||
107 | |||
108 | /* Search only direct children of the bus */ | ||
109 | for (dn = busdn->child; dn; dn = dn->sibling) | ||
110 | if (dn->devfn == devfn) | ||
111 | return rtas_read_config(dn, where, size, val); | ||
112 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
113 | } | ||
114 | |||
115 | static int rtas_write_config(struct device_node *dn, int where, int size, u32 val) | ||
116 | { | ||
117 | unsigned long buid, addr; | ||
118 | int ret; | ||
119 | |||
120 | if (!dn) | ||
121 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
122 | if (!config_access_valid(dn, where)) | ||
123 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
124 | |||
125 | addr = ((where & 0xf00) << 20) | (dn->busno << 16) | | ||
126 | (dn->devfn << 8) | (where & 0xff); | ||
127 | buid = dn->phb->buid; | ||
128 | if (buid) { | ||
129 | ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val); | ||
130 | } else { | ||
131 | ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val); | ||
132 | } | ||
133 | |||
134 | if (ret) | ||
135 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
136 | |||
137 | return PCIBIOS_SUCCESSFUL; | ||
138 | } | ||
139 | |||
140 | static int rtas_pci_write_config(struct pci_bus *bus, | ||
141 | unsigned int devfn, | ||
142 | int where, int size, u32 val) | ||
143 | { | ||
144 | struct device_node *busdn, *dn; | ||
145 | |||
146 | if (bus->self) | ||
147 | busdn = pci_device_to_OF_node(bus->self); | ||
148 | else | ||
149 | busdn = bus->sysdata; /* must be a phb */ | ||
150 | |||
151 | /* Search only direct children of the bus */ | ||
152 | for (dn = busdn->child; dn; dn = dn->sibling) | ||
153 | if (dn->devfn == devfn) | ||
154 | return rtas_write_config(dn, where, size, val); | ||
155 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
156 | } | ||
157 | |||
158 | struct pci_ops rtas_pci_ops = { | ||
159 | rtas_pci_read_config, | ||
160 | rtas_pci_write_config | ||
161 | }; | ||
162 | |||
163 | int is_python(struct device_node *dev) | ||
164 | { | ||
165 | char *model = (char *)get_property(dev, "model", NULL); | ||
166 | |||
167 | if (model && strstr(model, "Python")) | ||
168 | return 1; | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static int get_phb_reg_prop(struct device_node *dev, | ||
174 | unsigned int addr_size_words, | ||
175 | struct reg_property64 *reg) | ||
176 | { | ||
177 | unsigned int *ui_ptr = NULL, len; | ||
178 | |||
179 | /* Found a PHB, now figure out where his registers are mapped. */ | ||
180 | ui_ptr = (unsigned int *)get_property(dev, "reg", &len); | ||
181 | if (ui_ptr == NULL) | ||
182 | return 1; | ||
183 | |||
184 | if (addr_size_words == 1) { | ||
185 | reg->address = ((struct reg_property32 *)ui_ptr)->address; | ||
186 | reg->size = ((struct reg_property32 *)ui_ptr)->size; | ||
187 | } else { | ||
188 | *reg = *((struct reg_property64 *)ui_ptr); | ||
189 | } | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static void python_countermeasures(struct device_node *dev, | ||
195 | unsigned int addr_size_words) | ||
196 | { | ||
197 | struct reg_property64 reg_struct; | ||
198 | void __iomem *chip_regs; | ||
199 | volatile u32 val; | ||
200 | |||
201 | if (get_phb_reg_prop(dev, addr_size_words, ®_struct)) | ||
202 | return; | ||
203 | |||
204 | /* Python's register file is 1 MB in size. */ | ||
205 | chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); | ||
206 | |||
207 | /* | ||
208 | * Firmware doesn't always clear this bit which is critical | ||
209 | * for good performance - Anton | ||
210 | */ | ||
211 | |||
212 | #define PRG_CL_RESET_VALID 0x00010000 | ||
213 | |||
214 | val = in_be32(chip_regs + 0xf6030); | ||
215 | if (val & PRG_CL_RESET_VALID) { | ||
216 | printk(KERN_INFO "Python workaround: "); | ||
217 | val &= ~PRG_CL_RESET_VALID; | ||
218 | out_be32(chip_regs + 0xf6030, val); | ||
219 | /* | ||
220 | * We must read it back for changes to | ||
221 | * take effect | ||
222 | */ | ||
223 | val = in_be32(chip_regs + 0xf6030); | ||
224 | printk("reg0: %x\n", val); | ||
225 | } | ||
226 | |||
227 | iounmap(chip_regs); | ||
228 | } | ||
229 | |||
230 | void __init init_pci_config_tokens (void) | ||
231 | { | ||
232 | read_pci_config = rtas_token("read-pci-config"); | ||
233 | write_pci_config = rtas_token("write-pci-config"); | ||
234 | ibm_read_pci_config = rtas_token("ibm,read-pci-config"); | ||
235 | ibm_write_pci_config = rtas_token("ibm,write-pci-config"); | ||
236 | } | ||
237 | |||
238 | unsigned long __devinit get_phb_buid (struct device_node *phb) | ||
239 | { | ||
240 | int addr_cells; | ||
241 | unsigned int *buid_vals; | ||
242 | unsigned int len; | ||
243 | unsigned long buid; | ||
244 | |||
245 | if (ibm_read_pci_config == -1) return 0; | ||
246 | |||
247 | /* PHB's will always be children of the root node, | ||
248 | * or so it is promised by the current firmware. */ | ||
249 | if (phb->parent == NULL) | ||
250 | return 0; | ||
251 | if (phb->parent->parent) | ||
252 | return 0; | ||
253 | |||
254 | buid_vals = (unsigned int *) get_property(phb, "reg", &len); | ||
255 | if (buid_vals == NULL) | ||
256 | return 0; | ||
257 | |||
258 | addr_cells = prom_n_addr_cells(phb); | ||
259 | if (addr_cells == 1) { | ||
260 | buid = (unsigned long) buid_vals[0]; | ||
261 | } else { | ||
262 | buid = (((unsigned long)buid_vals[0]) << 32UL) | | ||
263 | (((unsigned long)buid_vals[1]) & 0xffffffff); | ||
264 | } | ||
265 | return buid; | ||
266 | } | ||
267 | |||
268 | static int phb_set_bus_ranges(struct device_node *dev, | ||
269 | struct pci_controller *phb) | ||
270 | { | ||
271 | int *bus_range; | ||
272 | unsigned int len; | ||
273 | |||
274 | bus_range = (int *) get_property(dev, "bus-range", &len); | ||
275 | if (bus_range == NULL || len < 2 * sizeof(int)) { | ||
276 | return 1; | ||
277 | } | ||
278 | |||
279 | phb->first_busno = bus_range[0]; | ||
280 | phb->last_busno = bus_range[1]; | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int __devinit setup_phb(struct device_node *dev, | ||
286 | struct pci_controller *phb, | ||
287 | unsigned int addr_size_words) | ||
288 | { | ||
289 | pci_setup_pci_controller(phb); | ||
290 | |||
291 | if (is_python(dev)) | ||
292 | python_countermeasures(dev, addr_size_words); | ||
293 | |||
294 | if (phb_set_bus_ranges(dev, phb)) | ||
295 | return 1; | ||
296 | |||
297 | phb->arch_data = dev; | ||
298 | phb->ops = &rtas_pci_ops; | ||
299 | phb->buid = get_phb_buid(dev); | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static void __devinit add_linux_pci_domain(struct device_node *dev, | ||
305 | struct pci_controller *phb, | ||
306 | struct property *of_prop) | ||
307 | { | ||
308 | memset(of_prop, 0, sizeof(struct property)); | ||
309 | of_prop->name = "linux,pci-domain"; | ||
310 | of_prop->length = sizeof(phb->global_number); | ||
311 | of_prop->value = (unsigned char *)&of_prop[1]; | ||
312 | memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number)); | ||
313 | prom_add_property(dev, of_prop); | ||
314 | } | ||
315 | |||
316 | static struct pci_controller * __init alloc_phb(struct device_node *dev, | ||
317 | unsigned int addr_size_words) | ||
318 | { | ||
319 | struct pci_controller *phb; | ||
320 | struct property *of_prop; | ||
321 | |||
322 | phb = alloc_bootmem(sizeof(struct pci_controller)); | ||
323 | if (phb == NULL) | ||
324 | return NULL; | ||
325 | |||
326 | of_prop = alloc_bootmem(sizeof(struct property) + | ||
327 | sizeof(phb->global_number)); | ||
328 | if (!of_prop) | ||
329 | return NULL; | ||
330 | |||
331 | if (setup_phb(dev, phb, addr_size_words)) | ||
332 | return NULL; | ||
333 | |||
334 | add_linux_pci_domain(dev, phb, of_prop); | ||
335 | |||
336 | return phb; | ||
337 | } | ||
338 | |||
339 | static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words) | ||
340 | { | ||
341 | struct pci_controller *phb; | ||
342 | |||
343 | phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), | ||
344 | GFP_KERNEL); | ||
345 | if (phb == NULL) | ||
346 | return NULL; | ||
347 | |||
348 | if (setup_phb(dev, phb, addr_size_words)) | ||
349 | return NULL; | ||
350 | |||
351 | phb->is_dynamic = 1; | ||
352 | |||
353 | /* TODO: linux,pci-domain? */ | ||
354 | |||
355 | return phb; | ||
356 | } | ||
357 | |||
358 | unsigned long __init find_and_init_phbs(void) | ||
359 | { | ||
360 | struct device_node *node; | ||
361 | struct pci_controller *phb; | ||
362 | unsigned int root_size_cells = 0; | ||
363 | unsigned int index; | ||
364 | unsigned int *opprop = NULL; | ||
365 | struct device_node *root = of_find_node_by_path("/"); | ||
366 | |||
367 | if (ppc64_interrupt_controller == IC_OPEN_PIC) { | ||
368 | opprop = (unsigned int *)get_property(root, | ||
369 | "platform-open-pic", NULL); | ||
370 | } | ||
371 | |||
372 | root_size_cells = prom_n_size_cells(root); | ||
373 | |||
374 | index = 0; | ||
375 | |||
376 | for (node = of_get_next_child(root, NULL); | ||
377 | node != NULL; | ||
378 | node = of_get_next_child(root, node)) { | ||
379 | if (node->type == NULL || strcmp(node->type, "pci") != 0) | ||
380 | continue; | ||
381 | |||
382 | phb = alloc_phb(node, root_size_cells); | ||
383 | if (!phb) | ||
384 | continue; | ||
385 | |||
386 | pci_process_bridge_OF_ranges(phb, node); | ||
387 | pci_setup_phb_io(phb, index == 0); | ||
388 | |||
389 | if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) { | ||
390 | int addr = root_size_cells * (index + 2) - 1; | ||
391 | mpic_assign_isu(pSeries_mpic, index, opprop[addr]); | ||
392 | } | ||
393 | |||
394 | index++; | ||
395 | } | ||
396 | |||
397 | of_node_put(root); | ||
398 | pci_devs_phb_init(); | ||
399 | |||
400 | /* | ||
401 | * pci_probe_only and pci_assign_all_buses can be set via properties | ||
402 | * in chosen. | ||
403 | */ | ||
404 | if (of_chosen) { | ||
405 | int *prop; | ||
406 | |||
407 | prop = (int *)get_property(of_chosen, "linux,pci-probe-only", | ||
408 | NULL); | ||
409 | if (prop) | ||
410 | pci_probe_only = *prop; | ||
411 | |||
412 | prop = (int *)get_property(of_chosen, | ||
413 | "linux,pci-assign-all-buses", NULL); | ||
414 | if (prop) | ||
415 | pci_assign_all_buses = *prop; | ||
416 | } | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) | ||
422 | { | ||
423 | struct device_node *root = of_find_node_by_path("/"); | ||
424 | unsigned int root_size_cells = 0; | ||
425 | struct pci_controller *phb; | ||
426 | struct pci_bus *bus; | ||
427 | int primary; | ||
428 | |||
429 | root_size_cells = prom_n_size_cells(root); | ||
430 | |||
431 | primary = list_empty(&hose_list); | ||
432 | phb = alloc_phb_dynamic(dn, root_size_cells); | ||
433 | if (!phb) | ||
434 | return NULL; | ||
435 | |||
436 | pci_process_bridge_OF_ranges(phb, dn); | ||
437 | |||
438 | pci_setup_phb_io_dynamic(phb, primary); | ||
439 | of_node_put(root); | ||
440 | |||
441 | pci_devs_phb_init_dynamic(phb); | ||
442 | phb->last_busno = 0xff; | ||
443 | bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data); | ||
444 | phb->bus = bus; | ||
445 | phb->last_busno = bus->subordinate; | ||
446 | |||
447 | return phb; | ||
448 | } | ||
449 | EXPORT_SYMBOL(init_phb_dynamic); | ||
450 | 36 | ||
451 | #if 0 | 37 | #if 0 |
452 | void pcibios_name_device(struct pci_dev *dev) | 38 | void pcibios_name_device(struct pci_dev *dev) |
@@ -474,7 +60,7 @@ void pcibios_name_device(struct pci_dev *dev) | |||
474 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device); | 60 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device); |
475 | #endif | 61 | #endif |
476 | 62 | ||
477 | static void check_s7a(void) | 63 | static void __init check_s7a(void) |
478 | { | 64 | { |
479 | struct device_node *root; | 65 | struct device_node *root; |
480 | char *model; | 66 | char *model; |
@@ -488,56 +74,6 @@ static void check_s7a(void) | |||
488 | } | 74 | } |
489 | } | 75 | } |
490 | 76 | ||
491 | /* RPA-specific bits for removing PHBs */ | ||
492 | int pcibios_remove_root_bus(struct pci_controller *phb) | ||
493 | { | ||
494 | struct pci_bus *b = phb->bus; | ||
495 | struct resource *res; | ||
496 | int rc, i; | ||
497 | |||
498 | res = b->resource[0]; | ||
499 | if (!res->flags) { | ||
500 | printk(KERN_ERR "%s: no IO resource for PHB %s\n", __FUNCTION__, | ||
501 | b->name); | ||
502 | return 1; | ||
503 | } | ||
504 | |||
505 | rc = unmap_bus_range(b); | ||
506 | if (rc) { | ||
507 | printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", | ||
508 | __FUNCTION__, b->name); | ||
509 | return 1; | ||
510 | } | ||
511 | |||
512 | if (release_resource(res)) { | ||
513 | printk(KERN_ERR "%s: failed to release IO on bus %s\n", | ||
514 | __FUNCTION__, b->name); | ||
515 | return 1; | ||
516 | } | ||
517 | |||
518 | for (i = 1; i < 3; ++i) { | ||
519 | res = b->resource[i]; | ||
520 | if (!res->flags && i == 0) { | ||
521 | printk(KERN_ERR "%s: no MEM resource for PHB %s\n", | ||
522 | __FUNCTION__, b->name); | ||
523 | return 1; | ||
524 | } | ||
525 | if (res->flags && release_resource(res)) { | ||
526 | printk(KERN_ERR | ||
527 | "%s: failed to release IO %d on bus %s\n", | ||
528 | __FUNCTION__, i, b->name); | ||
529 | return 1; | ||
530 | } | ||
531 | } | ||
532 | |||
533 | list_del(&phb->list_node); | ||
534 | if (phb->is_dynamic) | ||
535 | kfree(phb); | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | EXPORT_SYMBOL(pcibios_remove_root_bus); | ||
540 | |||
541 | static void __init pSeries_request_regions(void) | 77 | static void __init pSeries_request_regions(void) |
542 | { | 78 | { |
543 | if (!isa_io_base) | 79 | if (!isa_io_base) |
diff --git a/arch/ppc64/kernel/rtas_pci.c b/arch/ppc64/kernel/rtas_pci.c new file mode 100644 index 000000000000..1048817befb8 --- /dev/null +++ b/arch/ppc64/kernel/rtas_pci.c | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * arch/ppc64/kernel/rtas_pci.c | ||
3 | * | ||
4 | * Copyright (C) 2001 Dave Engebretsen, IBM Corporation | ||
5 | * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM | ||
6 | * | ||
7 | * RTAS specific routines for PCI. | ||
8 | * | ||
9 | * Based on code from pci.c, chrp_pci.c and pSeries_pci.c | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/threads.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/string.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/bootmem.h> | ||
32 | |||
33 | #include <asm/io.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | #include <asm/irq.h> | ||
36 | #include <asm/prom.h> | ||
37 | #include <asm/machdep.h> | ||
38 | #include <asm/pci-bridge.h> | ||
39 | #include <asm/iommu.h> | ||
40 | #include <asm/rtas.h> | ||
41 | |||
42 | #include "mpic.h" | ||
43 | #include "pci.h" | ||
44 | |||
45 | /* RTAS tokens */ | ||
46 | static int read_pci_config; | ||
47 | static int write_pci_config; | ||
48 | static int ibm_read_pci_config; | ||
49 | static int ibm_write_pci_config; | ||
50 | |||
51 | static int config_access_valid(struct device_node *dn, int where) | ||
52 | { | ||
53 | if (where < 256) | ||
54 | return 1; | ||
55 | if (where < 4096 && dn->pci_ext_config_space) | ||
56 | return 1; | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val) | ||
62 | { | ||
63 | int returnval = -1; | ||
64 | unsigned long buid, addr; | ||
65 | int ret; | ||
66 | |||
67 | if (!dn) | ||
68 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
69 | if (!config_access_valid(dn, where)) | ||
70 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
71 | |||
72 | addr = ((where & 0xf00) << 20) | (dn->busno << 16) | | ||
73 | (dn->devfn << 8) | (where & 0xff); | ||
74 | buid = dn->phb->buid; | ||
75 | if (buid) { | ||
76 | ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, | ||
77 | addr, buid >> 32, buid & 0xffffffff, size); | ||
78 | } else { | ||
79 | ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size); | ||
80 | } | ||
81 | *val = returnval; | ||
82 | |||
83 | if (ret) | ||
84 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
85 | |||
86 | if (returnval == EEH_IO_ERROR_VALUE(size) | ||
87 | && eeh_dn_check_failure (dn, NULL)) | ||
88 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
89 | |||
90 | return PCIBIOS_SUCCESSFUL; | ||
91 | } | ||
92 | |||
93 | static int rtas_pci_read_config(struct pci_bus *bus, | ||
94 | unsigned int devfn, | ||
95 | int where, int size, u32 *val) | ||
96 | { | ||
97 | struct device_node *busdn, *dn; | ||
98 | |||
99 | if (bus->self) | ||
100 | busdn = pci_device_to_OF_node(bus->self); | ||
101 | else | ||
102 | busdn = bus->sysdata; /* must be a phb */ | ||
103 | |||
104 | /* Search only direct children of the bus */ | ||
105 | for (dn = busdn->child; dn; dn = dn->sibling) | ||
106 | if (dn->devfn == devfn) | ||
107 | return rtas_read_config(dn, where, size, val); | ||
108 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
109 | } | ||
110 | |||
111 | static int rtas_write_config(struct device_node *dn, int where, int size, u32 val) | ||
112 | { | ||
113 | unsigned long buid, addr; | ||
114 | int ret; | ||
115 | |||
116 | if (!dn) | ||
117 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
118 | if (!config_access_valid(dn, where)) | ||
119 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
120 | |||
121 | addr = ((where & 0xf00) << 20) | (dn->busno << 16) | | ||
122 | (dn->devfn << 8) | (where & 0xff); | ||
123 | buid = dn->phb->buid; | ||
124 | if (buid) { | ||
125 | ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val); | ||
126 | } else { | ||
127 | ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val); | ||
128 | } | ||
129 | |||
130 | if (ret) | ||
131 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
132 | |||
133 | return PCIBIOS_SUCCESSFUL; | ||
134 | } | ||
135 | |||
136 | static int rtas_pci_write_config(struct pci_bus *bus, | ||
137 | unsigned int devfn, | ||
138 | int where, int size, u32 val) | ||
139 | { | ||
140 | struct device_node *busdn, *dn; | ||
141 | |||
142 | if (bus->self) | ||
143 | busdn = pci_device_to_OF_node(bus->self); | ||
144 | else | ||
145 | busdn = bus->sysdata; /* must be a phb */ | ||
146 | |||
147 | /* Search only direct children of the bus */ | ||
148 | for (dn = busdn->child; dn; dn = dn->sibling) | ||
149 | if (dn->devfn == devfn) | ||
150 | return rtas_write_config(dn, where, size, val); | ||
151 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
152 | } | ||
153 | |||
154 | struct pci_ops rtas_pci_ops = { | ||
155 | rtas_pci_read_config, | ||
156 | rtas_pci_write_config | ||
157 | }; | ||
158 | |||
159 | int is_python(struct device_node *dev) | ||
160 | { | ||
161 | char *model = (char *)get_property(dev, "model", NULL); | ||
162 | |||
163 | if (model && strstr(model, "Python")) | ||
164 | return 1; | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int get_phb_reg_prop(struct device_node *dev, | ||
170 | unsigned int addr_size_words, | ||
171 | struct reg_property64 *reg) | ||
172 | { | ||
173 | unsigned int *ui_ptr = NULL, len; | ||
174 | |||
175 | /* Found a PHB, now figure out where his registers are mapped. */ | ||
176 | ui_ptr = (unsigned int *)get_property(dev, "reg", &len); | ||
177 | if (ui_ptr == NULL) | ||
178 | return 1; | ||
179 | |||
180 | if (addr_size_words == 1) { | ||
181 | reg->address = ((struct reg_property32 *)ui_ptr)->address; | ||
182 | reg->size = ((struct reg_property32 *)ui_ptr)->size; | ||
183 | } else { | ||
184 | *reg = *((struct reg_property64 *)ui_ptr); | ||
185 | } | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static void python_countermeasures(struct device_node *dev, | ||
191 | unsigned int addr_size_words) | ||
192 | { | ||
193 | struct reg_property64 reg_struct; | ||
194 | void __iomem *chip_regs; | ||
195 | volatile u32 val; | ||
196 | |||
197 | if (get_phb_reg_prop(dev, addr_size_words, ®_struct)) | ||
198 | return; | ||
199 | |||
200 | /* Python's register file is 1 MB in size. */ | ||
201 | chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); | ||
202 | |||
203 | /* | ||
204 | * Firmware doesn't always clear this bit which is critical | ||
205 | * for good performance - Anton | ||
206 | */ | ||
207 | |||
208 | #define PRG_CL_RESET_VALID 0x00010000 | ||
209 | |||
210 | val = in_be32(chip_regs + 0xf6030); | ||
211 | if (val & PRG_CL_RESET_VALID) { | ||
212 | printk(KERN_INFO "Python workaround: "); | ||
213 | val &= ~PRG_CL_RESET_VALID; | ||
214 | out_be32(chip_regs + 0xf6030, val); | ||
215 | /* | ||
216 | * We must read it back for changes to | ||
217 | * take effect | ||
218 | */ | ||
219 | val = in_be32(chip_regs + 0xf6030); | ||
220 | printk("reg0: %x\n", val); | ||
221 | } | ||
222 | |||
223 | iounmap(chip_regs); | ||
224 | } | ||
225 | |||
226 | void __init init_pci_config_tokens (void) | ||
227 | { | ||
228 | read_pci_config = rtas_token("read-pci-config"); | ||
229 | write_pci_config = rtas_token("write-pci-config"); | ||
230 | ibm_read_pci_config = rtas_token("ibm,read-pci-config"); | ||
231 | ibm_write_pci_config = rtas_token("ibm,write-pci-config"); | ||
232 | } | ||
233 | |||
234 | unsigned long __devinit get_phb_buid (struct device_node *phb) | ||
235 | { | ||
236 | int addr_cells; | ||
237 | unsigned int *buid_vals; | ||
238 | unsigned int len; | ||
239 | unsigned long buid; | ||
240 | |||
241 | if (ibm_read_pci_config == -1) return 0; | ||
242 | |||
243 | /* PHB's will always be children of the root node, | ||
244 | * or so it is promised by the current firmware. */ | ||
245 | if (phb->parent == NULL) | ||
246 | return 0; | ||
247 | if (phb->parent->parent) | ||
248 | return 0; | ||
249 | |||
250 | buid_vals = (unsigned int *) get_property(phb, "reg", &len); | ||
251 | if (buid_vals == NULL) | ||
252 | return 0; | ||
253 | |||
254 | addr_cells = prom_n_addr_cells(phb); | ||
255 | if (addr_cells == 1) { | ||
256 | buid = (unsigned long) buid_vals[0]; | ||
257 | } else { | ||
258 | buid = (((unsigned long)buid_vals[0]) << 32UL) | | ||
259 | (((unsigned long)buid_vals[1]) & 0xffffffff); | ||
260 | } | ||
261 | return buid; | ||
262 | } | ||
263 | |||
264 | static int phb_set_bus_ranges(struct device_node *dev, | ||
265 | struct pci_controller *phb) | ||
266 | { | ||
267 | int *bus_range; | ||
268 | unsigned int len; | ||
269 | |||
270 | bus_range = (int *) get_property(dev, "bus-range", &len); | ||
271 | if (bus_range == NULL || len < 2 * sizeof(int)) { | ||
272 | return 1; | ||
273 | } | ||
274 | |||
275 | phb->first_busno = bus_range[0]; | ||
276 | phb->last_busno = bus_range[1]; | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | static int __devinit setup_phb(struct device_node *dev, | ||
282 | struct pci_controller *phb, | ||
283 | unsigned int addr_size_words) | ||
284 | { | ||
285 | pci_setup_pci_controller(phb); | ||
286 | |||
287 | if (is_python(dev)) | ||
288 | python_countermeasures(dev, addr_size_words); | ||
289 | |||
290 | if (phb_set_bus_ranges(dev, phb)) | ||
291 | return 1; | ||
292 | |||
293 | phb->arch_data = dev; | ||
294 | phb->ops = &rtas_pci_ops; | ||
295 | phb->buid = get_phb_buid(dev); | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static void __devinit add_linux_pci_domain(struct device_node *dev, | ||
301 | struct pci_controller *phb, | ||
302 | struct property *of_prop) | ||
303 | { | ||
304 | memset(of_prop, 0, sizeof(struct property)); | ||
305 | of_prop->name = "linux,pci-domain"; | ||
306 | of_prop->length = sizeof(phb->global_number); | ||
307 | of_prop->value = (unsigned char *)&of_prop[1]; | ||
308 | memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number)); | ||
309 | prom_add_property(dev, of_prop); | ||
310 | } | ||
311 | |||
312 | static struct pci_controller * __init alloc_phb(struct device_node *dev, | ||
313 | unsigned int addr_size_words) | ||
314 | { | ||
315 | struct pci_controller *phb; | ||
316 | struct property *of_prop; | ||
317 | |||
318 | phb = alloc_bootmem(sizeof(struct pci_controller)); | ||
319 | if (phb == NULL) | ||
320 | return NULL; | ||
321 | |||
322 | of_prop = alloc_bootmem(sizeof(struct property) + | ||
323 | sizeof(phb->global_number)); | ||
324 | if (!of_prop) | ||
325 | return NULL; | ||
326 | |||
327 | if (setup_phb(dev, phb, addr_size_words)) | ||
328 | return NULL; | ||
329 | |||
330 | add_linux_pci_domain(dev, phb, of_prop); | ||
331 | |||
332 | return phb; | ||
333 | } | ||
334 | |||
335 | static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words) | ||
336 | { | ||
337 | struct pci_controller *phb; | ||
338 | |||
339 | phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), | ||
340 | GFP_KERNEL); | ||
341 | if (phb == NULL) | ||
342 | return NULL; | ||
343 | |||
344 | if (setup_phb(dev, phb, addr_size_words)) | ||
345 | return NULL; | ||
346 | |||
347 | phb->is_dynamic = 1; | ||
348 | |||
349 | /* TODO: linux,pci-domain? */ | ||
350 | |||
351 | return phb; | ||
352 | } | ||
353 | |||
354 | unsigned long __init find_and_init_phbs(void) | ||
355 | { | ||
356 | struct device_node *node; | ||
357 | struct pci_controller *phb; | ||
358 | unsigned int root_size_cells = 0; | ||
359 | unsigned int index; | ||
360 | unsigned int *opprop = NULL; | ||
361 | struct device_node *root = of_find_node_by_path("/"); | ||
362 | |||
363 | if (ppc64_interrupt_controller == IC_OPEN_PIC) { | ||
364 | opprop = (unsigned int *)get_property(root, | ||
365 | "platform-open-pic", NULL); | ||
366 | } | ||
367 | |||
368 | root_size_cells = prom_n_size_cells(root); | ||
369 | |||
370 | index = 0; | ||
371 | |||
372 | for (node = of_get_next_child(root, NULL); | ||
373 | node != NULL; | ||
374 | node = of_get_next_child(root, node)) { | ||
375 | if (node->type == NULL || strcmp(node->type, "pci") != 0) | ||
376 | continue; | ||
377 | |||
378 | phb = alloc_phb(node, root_size_cells); | ||
379 | if (!phb) | ||
380 | continue; | ||
381 | |||
382 | pci_process_bridge_OF_ranges(phb, node); | ||
383 | pci_setup_phb_io(phb, index == 0); | ||
384 | #ifdef CONFIG_PPC_PSERIES | ||
385 | if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) { | ||
386 | int addr = root_size_cells * (index + 2) - 1; | ||
387 | mpic_assign_isu(pSeries_mpic, index, opprop[addr]); | ||
388 | } | ||
389 | #endif | ||
390 | index++; | ||
391 | } | ||
392 | |||
393 | of_node_put(root); | ||
394 | pci_devs_phb_init(); | ||
395 | |||
396 | /* | ||
397 | * pci_probe_only and pci_assign_all_buses can be set via properties | ||
398 | * in chosen. | ||
399 | */ | ||
400 | if (of_chosen) { | ||
401 | int *prop; | ||
402 | |||
403 | prop = (int *)get_property(of_chosen, "linux,pci-probe-only", | ||
404 | NULL); | ||
405 | if (prop) | ||
406 | pci_probe_only = *prop; | ||
407 | |||
408 | prop = (int *)get_property(of_chosen, | ||
409 | "linux,pci-assign-all-buses", NULL); | ||
410 | if (prop) | ||
411 | pci_assign_all_buses = *prop; | ||
412 | } | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) | ||
418 | { | ||
419 | struct device_node *root = of_find_node_by_path("/"); | ||
420 | unsigned int root_size_cells = 0; | ||
421 | struct pci_controller *phb; | ||
422 | struct pci_bus *bus; | ||
423 | int primary; | ||
424 | |||
425 | root_size_cells = prom_n_size_cells(root); | ||
426 | |||
427 | primary = list_empty(&hose_list); | ||
428 | phb = alloc_phb_dynamic(dn, root_size_cells); | ||
429 | if (!phb) | ||
430 | return NULL; | ||
431 | |||
432 | pci_process_bridge_OF_ranges(phb, dn); | ||
433 | |||
434 | pci_setup_phb_io_dynamic(phb, primary); | ||
435 | of_node_put(root); | ||
436 | |||
437 | pci_devs_phb_init_dynamic(phb); | ||
438 | phb->last_busno = 0xff; | ||
439 | bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data); | ||
440 | phb->bus = bus; | ||
441 | phb->last_busno = bus->subordinate; | ||
442 | |||
443 | return phb; | ||
444 | } | ||
445 | EXPORT_SYMBOL(init_phb_dynamic); | ||
446 | |||
447 | /* RPA-specific bits for removing PHBs */ | ||
448 | int pcibios_remove_root_bus(struct pci_controller *phb) | ||
449 | { | ||
450 | struct pci_bus *b = phb->bus; | ||
451 | struct resource *res; | ||
452 | int rc, i; | ||
453 | |||
454 | res = b->resource[0]; | ||
455 | if (!res->flags) { | ||
456 | printk(KERN_ERR "%s: no IO resource for PHB %s\n", __FUNCTION__, | ||
457 | b->name); | ||
458 | return 1; | ||
459 | } | ||
460 | |||
461 | rc = unmap_bus_range(b); | ||
462 | if (rc) { | ||
463 | printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", | ||
464 | __FUNCTION__, b->name); | ||
465 | return 1; | ||
466 | } | ||
467 | |||
468 | if (release_resource(res)) { | ||
469 | printk(KERN_ERR "%s: failed to release IO on bus %s\n", | ||
470 | __FUNCTION__, b->name); | ||
471 | return 1; | ||
472 | } | ||
473 | |||
474 | for (i = 1; i < 3; ++i) { | ||
475 | res = b->resource[i]; | ||
476 | if (!res->flags && i == 0) { | ||
477 | printk(KERN_ERR "%s: no MEM resource for PHB %s\n", | ||
478 | __FUNCTION__, b->name); | ||
479 | return 1; | ||
480 | } | ||
481 | if (res->flags && release_resource(res)) { | ||
482 | printk(KERN_ERR | ||
483 | "%s: failed to release IO %d on bus %s\n", | ||
484 | __FUNCTION__, i, b->name); | ||
485 | return 1; | ||
486 | } | ||
487 | } | ||
488 | |||
489 | list_del(&phb->list_node); | ||
490 | if (phb->is_dynamic) | ||
491 | kfree(phb); | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | EXPORT_SYMBOL(pcibios_remove_root_bus); | ||