diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc64/kernel/pSeries_pci.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ppc64/kernel/pSeries_pci.c')
-rw-r--r-- | arch/ppc64/kernel/pSeries_pci.c | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/pSeries_pci.c b/arch/ppc64/kernel/pSeries_pci.c new file mode 100644 index 000000000000..0b1cca281408 --- /dev/null +++ b/arch/ppc64/kernel/pSeries_pci.c | |||
@@ -0,0 +1,602 @@ | |||
1 | /* | ||
2 | * pSeries_pci.c | ||
3 | * | ||
4 | * Copyright (C) 2001 Dave Engebretsen, IBM Corporation | ||
5 | * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM | ||
6 | * | ||
7 | * pSeries specific routines for PCI. | ||
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 | ||
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 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 | |||
451 | #if 0 | ||
452 | void pcibios_name_device(struct pci_dev *dev) | ||
453 | { | ||
454 | struct device_node *dn; | ||
455 | |||
456 | /* | ||
457 | * Add IBM loc code (slot) as a prefix to the device names for service | ||
458 | */ | ||
459 | dn = pci_device_to_OF_node(dev); | ||
460 | if (dn) { | ||
461 | char *loc_code = get_property(dn, "ibm,loc-code", 0); | ||
462 | if (loc_code) { | ||
463 | int loc_len = strlen(loc_code); | ||
464 | if (loc_len < sizeof(dev->dev.name)) { | ||
465 | memmove(dev->dev.name+loc_len+1, dev->dev.name, | ||
466 | sizeof(dev->dev.name)-loc_len-1); | ||
467 | memcpy(dev->dev.name, loc_code, loc_len); | ||
468 | dev->dev.name[loc_len] = ' '; | ||
469 | dev->dev.name[sizeof(dev->dev.name)-1] = '\0'; | ||
470 | } | ||
471 | } | ||
472 | } | ||
473 | } | ||
474 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device); | ||
475 | #endif | ||
476 | |||
477 | static void check_s7a(void) | ||
478 | { | ||
479 | struct device_node *root; | ||
480 | char *model; | ||
481 | |||
482 | root = of_find_node_by_path("/"); | ||
483 | if (root) { | ||
484 | model = get_property(root, "model", NULL); | ||
485 | if (model && !strcmp(model, "IBM,7013-S7A")) | ||
486 | s7a_workaround = 1; | ||
487 | of_node_put(root); | ||
488 | } | ||
489 | } | ||
490 | |||
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) | ||
542 | { | ||
543 | if (!isa_io_base) | ||
544 | return; | ||
545 | |||
546 | request_region(0x20,0x20,"pic1"); | ||
547 | request_region(0xa0,0x20,"pic2"); | ||
548 | request_region(0x00,0x20,"dma1"); | ||
549 | request_region(0x40,0x20,"timer"); | ||
550 | request_region(0x80,0x10,"dma page reg"); | ||
551 | request_region(0xc0,0x20,"dma2"); | ||
552 | } | ||
553 | |||
554 | void __init pSeries_final_fixup(void) | ||
555 | { | ||
556 | struct pci_dev *dev = NULL; | ||
557 | |||
558 | check_s7a(); | ||
559 | |||
560 | for_each_pci_dev(dev) { | ||
561 | pci_read_irq_line(dev); | ||
562 | if (s7a_workaround) { | ||
563 | if (dev->irq > 16) { | ||
564 | dev->irq -= 3; | ||
565 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); | ||
566 | } | ||
567 | } | ||
568 | } | ||
569 | |||
570 | phbs_remap_io(); | ||
571 | pSeries_request_regions(); | ||
572 | |||
573 | pci_addr_cache_build(); | ||
574 | } | ||
575 | |||
576 | /* | ||
577 | * Assume the winbond 82c105 is the IDE controller on a | ||
578 | * p610. We should probably be more careful in case | ||
579 | * someone tries to plug in a similar adapter. | ||
580 | */ | ||
581 | static void fixup_winbond_82c105(struct pci_dev* dev) | ||
582 | { | ||
583 | int i; | ||
584 | unsigned int reg; | ||
585 | |||
586 | if (!(systemcfg->platform & PLATFORM_PSERIES)) | ||
587 | return; | ||
588 | |||
589 | printk("Using INTC for W82c105 IDE controller.\n"); | ||
590 | pci_read_config_dword(dev, 0x40, ®); | ||
591 | /* Enable LEGIRQ to use INTC instead of ISA interrupts */ | ||
592 | pci_write_config_dword(dev, 0x40, reg | (1<<11)); | ||
593 | |||
594 | for (i = 0; i < DEVICE_COUNT_RESOURCE; ++i) { | ||
595 | /* zap the 2nd function of the winbond chip */ | ||
596 | if (dev->resource[i].flags & IORESOURCE_IO | ||
597 | && dev->bus->number == 0 && dev->devfn == 0x81) | ||
598 | dev->resource[i].flags &= ~IORESOURCE_IO; | ||
599 | } | ||
600 | } | ||
601 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, | ||
602 | fixup_winbond_82c105); | ||