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/rtas_pci.c | |
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/rtas_pci.c')
-rw-r--r-- | arch/ppc64/kernel/rtas_pci.c | 495 |
1 files changed, 495 insertions, 0 deletions
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); | ||