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/frv/mb93090-mb00/pci-vdk.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/frv/mb93090-mb00/pci-vdk.c')
-rw-r--r-- | arch/frv/mb93090-mb00/pci-vdk.c | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/arch/frv/mb93090-mb00/pci-vdk.c b/arch/frv/mb93090-mb00/pci-vdk.c new file mode 100644 index 000000000000..c8817f7b8605 --- /dev/null +++ b/arch/frv/mb93090-mb00/pci-vdk.c | |||
@@ -0,0 +1,467 @@ | |||
1 | /* pci-vdk.c: MB93090-MB00 (VDK) PCI support | ||
2 | * | ||
3 | * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/ioport.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | #include <asm/segment.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/mb-regs.h> | ||
25 | #include <asm/mb86943a.h> | ||
26 | #include "pci-frv.h" | ||
27 | |||
28 | unsigned int __nongpreldata pci_probe = 1; | ||
29 | |||
30 | int __nongpreldata pcibios_last_bus = -1; | ||
31 | struct pci_bus *__nongpreldata pci_root_bus; | ||
32 | struct pci_ops *__nongpreldata pci_root_ops; | ||
33 | |||
34 | /* | ||
35 | * Functions for accessing PCI configuration space | ||
36 | */ | ||
37 | |||
38 | #define CONFIG_CMD(bus, dev, where) \ | ||
39 | (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) | ||
40 | |||
41 | #define __set_PciCfgAddr(A) writel((A), (volatile void __iomem *) __region_CS1 + 0x80) | ||
42 | |||
43 | #define __get_PciCfgDataB(A) readb((volatile void __iomem *) __region_CS1 + 0x88 + ((A) & 3)) | ||
44 | #define __get_PciCfgDataW(A) readw((volatile void __iomem *) __region_CS1 + 0x88 + ((A) & 2)) | ||
45 | #define __get_PciCfgDataL(A) readl((volatile void __iomem *) __region_CS1 + 0x88) | ||
46 | |||
47 | #define __set_PciCfgDataB(A,V) \ | ||
48 | writeb((V), (volatile void __iomem *) __region_CS1 + 0x88 + (3 - ((A) & 3))) | ||
49 | |||
50 | #define __set_PciCfgDataW(A,V) \ | ||
51 | writew((V), (volatile void __iomem *) __region_CS1 + 0x88 + (2 - ((A) & 2))) | ||
52 | |||
53 | #define __set_PciCfgDataL(A,V) \ | ||
54 | writel((V), (volatile void __iomem *) __region_CS1 + 0x88) | ||
55 | |||
56 | #define __get_PciBridgeDataB(A) readb((volatile void __iomem *) __region_CS1 + 0x800 + (A)) | ||
57 | #define __get_PciBridgeDataW(A) readw((volatile void __iomem *) __region_CS1 + 0x800 + (A)) | ||
58 | #define __get_PciBridgeDataL(A) readl((volatile void __iomem *) __region_CS1 + 0x800 + (A)) | ||
59 | |||
60 | #define __set_PciBridgeDataB(A,V) writeb((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A)) | ||
61 | #define __set_PciBridgeDataW(A,V) writew((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A)) | ||
62 | #define __set_PciBridgeDataL(A,V) writel((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A)) | ||
63 | |||
64 | static inline int __query(const struct pci_dev *dev) | ||
65 | { | ||
66 | // return dev->bus->number==0 && (dev->devfn==PCI_DEVFN(0,0)); | ||
67 | // return dev->bus->number==1; | ||
68 | // return dev->bus->number==0 && | ||
69 | // (dev->devfn==PCI_DEVFN(2,0) || dev->devfn==PCI_DEVFN(3,0)); | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | /*****************************************************************************/ | ||
74 | /* | ||
75 | * | ||
76 | */ | ||
77 | static int pci_frv_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size, | ||
78 | u32 *val) | ||
79 | { | ||
80 | u32 _value; | ||
81 | |||
82 | if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { | ||
83 | _value = __get_PciBridgeDataL(where & ~3); | ||
84 | } | ||
85 | else { | ||
86 | __set_PciCfgAddr(CONFIG_CMD(bus, devfn, where)); | ||
87 | _value = __get_PciCfgDataL(where & ~3); | ||
88 | } | ||
89 | |||
90 | switch (size) { | ||
91 | case 1: | ||
92 | _value = _value >> ((where & 3) * 8); | ||
93 | break; | ||
94 | |||
95 | case 2: | ||
96 | _value = _value >> ((where & 2) * 8); | ||
97 | break; | ||
98 | |||
99 | case 4: | ||
100 | break; | ||
101 | |||
102 | default: | ||
103 | BUG(); | ||
104 | } | ||
105 | |||
106 | *val = _value; | ||
107 | return PCIBIOS_SUCCESSFUL; | ||
108 | } | ||
109 | |||
110 | static int pci_frv_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, | ||
111 | u32 value) | ||
112 | { | ||
113 | switch (size) { | ||
114 | case 1: | ||
115 | if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { | ||
116 | __set_PciBridgeDataB(where, value); | ||
117 | } | ||
118 | else { | ||
119 | __set_PciCfgAddr(CONFIG_CMD(bus, devfn, where)); | ||
120 | __set_PciCfgDataB(where, value); | ||
121 | } | ||
122 | break; | ||
123 | |||
124 | case 2: | ||
125 | if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { | ||
126 | __set_PciBridgeDataW(where, value); | ||
127 | } | ||
128 | else { | ||
129 | __set_PciCfgAddr(CONFIG_CMD(bus, devfn, where)); | ||
130 | __set_PciCfgDataW(where, value); | ||
131 | } | ||
132 | break; | ||
133 | |||
134 | case 4: | ||
135 | if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { | ||
136 | __set_PciBridgeDataL(where, value); | ||
137 | } | ||
138 | else { | ||
139 | __set_PciCfgAddr(CONFIG_CMD(bus, devfn, where)); | ||
140 | __set_PciCfgDataL(where, value); | ||
141 | } | ||
142 | break; | ||
143 | |||
144 | default: | ||
145 | BUG(); | ||
146 | } | ||
147 | |||
148 | return PCIBIOS_SUCCESSFUL; | ||
149 | } | ||
150 | |||
151 | static struct pci_ops pci_direct_frv = { | ||
152 | pci_frv_read_config, | ||
153 | pci_frv_write_config, | ||
154 | }; | ||
155 | |||
156 | /* | ||
157 | * Before we decide to use direct hardware access mechanisms, we try to do some | ||
158 | * trivial checks to ensure it at least _seems_ to be working -- we just test | ||
159 | * whether bus 00 contains a host bridge (this is similar to checking | ||
160 | * techniques used in XFree86, but ours should be more reliable since we | ||
161 | * attempt to make use of direct access hints provided by the PCI BIOS). | ||
162 | * | ||
163 | * This should be close to trivial, but it isn't, because there are buggy | ||
164 | * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. | ||
165 | */ | ||
166 | static int __init pci_sanity_check(struct pci_ops *o) | ||
167 | { | ||
168 | struct pci_bus bus; /* Fake bus and device */ | ||
169 | u32 id; | ||
170 | |||
171 | bus.number = 0; | ||
172 | |||
173 | if (o->read(&bus, 0, PCI_VENDOR_ID, 4, &id) == PCIBIOS_SUCCESSFUL) { | ||
174 | printk("PCI: VDK Bridge device:vendor: %08x\n", id); | ||
175 | if (id == 0x200e10cf) | ||
176 | return 1; | ||
177 | } | ||
178 | |||
179 | printk("PCI: VDK Bridge: Sanity check failed\n"); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static struct pci_ops * __init pci_check_direct(void) | ||
184 | { | ||
185 | unsigned long flags; | ||
186 | |||
187 | local_irq_save(flags); | ||
188 | |||
189 | /* check if access works */ | ||
190 | if (pci_sanity_check(&pci_direct_frv)) { | ||
191 | local_irq_restore(flags); | ||
192 | printk("PCI: Using configuration frv\n"); | ||
193 | // request_mem_region(0xBE040000, 256, "FRV bridge"); | ||
194 | // request_mem_region(0xBFFFFFF4, 12, "PCI frv"); | ||
195 | return &pci_direct_frv; | ||
196 | } | ||
197 | |||
198 | local_irq_restore(flags); | ||
199 | return NULL; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * Several buggy motherboards address only 16 devices and mirror | ||
204 | * them to next 16 IDs. We try to detect this `feature' on all | ||
205 | * primary buses (those containing host bridges as they are | ||
206 | * expected to be unique) and remove the ghost devices. | ||
207 | */ | ||
208 | |||
209 | static void __init pcibios_fixup_ghosts(struct pci_bus *b) | ||
210 | { | ||
211 | struct list_head *ln, *mn; | ||
212 | struct pci_dev *d, *e; | ||
213 | int mirror = PCI_DEVFN(16,0); | ||
214 | int seen_host_bridge = 0; | ||
215 | int i; | ||
216 | |||
217 | for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { | ||
218 | d = pci_dev_b(ln); | ||
219 | if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) | ||
220 | seen_host_bridge++; | ||
221 | for (mn=ln->next; mn != &b->devices; mn=mn->next) { | ||
222 | e = pci_dev_b(mn); | ||
223 | if (e->devfn != d->devfn + mirror || | ||
224 | e->vendor != d->vendor || | ||
225 | e->device != d->device || | ||
226 | e->class != d->class) | ||
227 | continue; | ||
228 | for(i=0; i<PCI_NUM_RESOURCES; i++) | ||
229 | if (e->resource[i].start != d->resource[i].start || | ||
230 | e->resource[i].end != d->resource[i].end || | ||
231 | e->resource[i].flags != d->resource[i].flags) | ||
232 | continue; | ||
233 | break; | ||
234 | } | ||
235 | if (mn == &b->devices) | ||
236 | return; | ||
237 | } | ||
238 | if (!seen_host_bridge) | ||
239 | return; | ||
240 | printk("PCI: Ignoring ghost devices on bus %02x\n", b->number); | ||
241 | |||
242 | ln = &b->devices; | ||
243 | while (ln->next != &b->devices) { | ||
244 | d = pci_dev_b(ln->next); | ||
245 | if (d->devfn >= mirror) { | ||
246 | list_del(&d->global_list); | ||
247 | list_del(&d->bus_list); | ||
248 | kfree(d); | ||
249 | } else | ||
250 | ln = ln->next; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * Discover remaining PCI buses in case there are peer host bridges. | ||
256 | * We use the number of last PCI bus provided by the PCI BIOS. | ||
257 | */ | ||
258 | static void __init pcibios_fixup_peer_bridges(void) | ||
259 | { | ||
260 | struct pci_bus bus; | ||
261 | struct pci_dev dev; | ||
262 | int n; | ||
263 | u16 l; | ||
264 | |||
265 | if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff) | ||
266 | return; | ||
267 | printk("PCI: Peer bridge fixup\n"); | ||
268 | for (n=0; n <= pcibios_last_bus; n++) { | ||
269 | if (pci_find_bus(0, n)) | ||
270 | continue; | ||
271 | bus.number = n; | ||
272 | bus.ops = pci_root_ops; | ||
273 | dev.bus = &bus; | ||
274 | for(dev.devfn=0; dev.devfn<256; dev.devfn += 8) | ||
275 | if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) && | ||
276 | l != 0x0000 && l != 0xffff) { | ||
277 | printk("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l); | ||
278 | printk("PCI: Discovered peer bus %02x\n", n); | ||
279 | pci_scan_bus(n, pci_root_ops, NULL); | ||
280 | break; | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /* | ||
286 | * Exceptions for specific devices. Usually work-arounds for fatal design flaws. | ||
287 | */ | ||
288 | |||
289 | static void __init pci_fixup_umc_ide(struct pci_dev *d) | ||
290 | { | ||
291 | /* | ||
292 | * UM8886BF IDE controller sets region type bits incorrectly, | ||
293 | * therefore they look like memory despite of them being I/O. | ||
294 | */ | ||
295 | int i; | ||
296 | |||
297 | printk("PCI: Fixing base address flags for device %s\n", pci_name(d)); | ||
298 | for(i=0; i<4; i++) | ||
299 | d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; | ||
300 | } | ||
301 | |||
302 | static void __init pci_fixup_ide_bases(struct pci_dev *d) | ||
303 | { | ||
304 | int i; | ||
305 | |||
306 | /* | ||
307 | * PCI IDE controllers use non-standard I/O port decoding, respect it. | ||
308 | */ | ||
309 | if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE) | ||
310 | return; | ||
311 | printk("PCI: IDE base address fixup for %s\n", pci_name(d)); | ||
312 | for(i=0; i<4; i++) { | ||
313 | struct resource *r = &d->resource[i]; | ||
314 | if ((r->start & ~0x80) == 0x374) { | ||
315 | r->start |= 2; | ||
316 | r->end = r->start; | ||
317 | } | ||
318 | } | ||
319 | } | ||
320 | |||
321 | static void __init pci_fixup_ide_trash(struct pci_dev *d) | ||
322 | { | ||
323 | int i; | ||
324 | |||
325 | /* | ||
326 | * There exist PCI IDE controllers which have utter garbage | ||
327 | * in first four base registers. Ignore that. | ||
328 | */ | ||
329 | printk("PCI: IDE base address trash cleared for %s\n", pci_name(d)); | ||
330 | for(i=0; i<4; i++) | ||
331 | d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0; | ||
332 | } | ||
333 | |||
334 | static void __devinit pci_fixup_latency(struct pci_dev *d) | ||
335 | { | ||
336 | /* | ||
337 | * SiS 5597 and 5598 chipsets require latency timer set to | ||
338 | * at most 32 to avoid lockups. | ||
339 | */ | ||
340 | DBG("PCI: Setting max latency to 32\n"); | ||
341 | pcibios_max_latency = 32; | ||
342 | } | ||
343 | |||
344 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide); | ||
345 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash); | ||
346 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, pci_fixup_latency); | ||
347 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency); | ||
348 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases); | ||
349 | |||
350 | /* | ||
351 | * Called after each bus is probed, but before its children | ||
352 | * are examined. | ||
353 | */ | ||
354 | |||
355 | void __init pcibios_fixup_bus(struct pci_bus *bus) | ||
356 | { | ||
357 | #if 0 | ||
358 | printk("### PCIBIOS_FIXUP_BUS(%d)\n",bus->number); | ||
359 | #endif | ||
360 | pcibios_fixup_ghosts(bus); | ||
361 | pci_read_bridge_bases(bus); | ||
362 | |||
363 | if (bus->number == 0) { | ||
364 | struct list_head *ln; | ||
365 | struct pci_dev *dev; | ||
366 | for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) { | ||
367 | dev = pci_dev_b(ln); | ||
368 | if (dev->devfn == 0) { | ||
369 | dev->resource[0].start = 0; | ||
370 | dev->resource[0].end = 0; | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Initialization. Try all known PCI access methods. Note that we support | ||
378 | * using both PCI BIOS and direct access: in such cases, we use I/O ports | ||
379 | * to access config space, but we still keep BIOS order of cards to be | ||
380 | * compatible with 2.0.X. This should go away some day. | ||
381 | */ | ||
382 | |||
383 | int __init pcibios_init(void) | ||
384 | { | ||
385 | struct pci_ops *dir = NULL; | ||
386 | |||
387 | if (!mb93090_mb00_detected) | ||
388 | return -ENXIO; | ||
389 | |||
390 | __reg_MB86943_sl_ctl |= MB86943_SL_CTL_DRCT_MASTER_SWAP | MB86943_SL_CTL_DRCT_SLAVE_SWAP; | ||
391 | |||
392 | __reg_MB86943_ecs_base(1) = ((__region_CS2 + 0x01000000) >> 9) | 0x08000000; | ||
393 | __reg_MB86943_ecs_base(2) = ((__region_CS2 + 0x00000000) >> 9) | 0x08000000; | ||
394 | |||
395 | *(volatile uint32_t *) (__region_CS1 + 0x848) = 0xe0000000; | ||
396 | *(volatile uint32_t *) (__region_CS1 + 0x8b8) = 0x00000000; | ||
397 | |||
398 | __reg_MB86943_sl_pci_io_base = (__region_CS2 + 0x04000000) >> 9; | ||
399 | __reg_MB86943_sl_pci_mem_base = (__region_CS2 + 0x08000000) >> 9; | ||
400 | __reg_MB86943_pci_sl_io_base = __region_CS2 + 0x04000000; | ||
401 | __reg_MB86943_pci_sl_mem_base = __region_CS2 + 0x08000000; | ||
402 | mb(); | ||
403 | |||
404 | *(volatile unsigned long *)(__region_CS2+0x01300014) == 1; | ||
405 | |||
406 | ioport_resource.start = (__reg_MB86943_sl_pci_io_base << 9) & 0xfffffc00; | ||
407 | ioport_resource.end = (__reg_MB86943_sl_pci_io_range << 9) | 0x3ff; | ||
408 | ioport_resource.end += ioport_resource.start; | ||
409 | |||
410 | printk("PCI IO window: %08lx-%08lx\n", ioport_resource.start, ioport_resource.end); | ||
411 | |||
412 | iomem_resource.start = (__reg_MB86943_sl_pci_mem_base << 9) & 0xfffffc00; | ||
413 | |||
414 | /* Reserve somewhere to write to flush posted writes. */ | ||
415 | iomem_resource.start += 0x400; | ||
416 | |||
417 | iomem_resource.end = (__reg_MB86943_sl_pci_mem_range << 9) | 0x3ff; | ||
418 | iomem_resource.end += iomem_resource.start; | ||
419 | |||
420 | printk("PCI MEM window: %08lx-%08lx\n", iomem_resource.start, iomem_resource.end); | ||
421 | printk("PCI DMA memory: %08lx-%08lx\n", dma_coherent_mem_start, dma_coherent_mem_end); | ||
422 | |||
423 | if (!pci_probe) | ||
424 | return -ENXIO; | ||
425 | |||
426 | dir = pci_check_direct(); | ||
427 | if (dir) | ||
428 | pci_root_ops = dir; | ||
429 | else { | ||
430 | printk("PCI: No PCI bus detected\n"); | ||
431 | return -ENXIO; | ||
432 | } | ||
433 | |||
434 | printk("PCI: Probing PCI hardware\n"); | ||
435 | pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL); | ||
436 | |||
437 | pcibios_irq_init(); | ||
438 | pcibios_fixup_peer_bridges(); | ||
439 | pcibios_fixup_irqs(); | ||
440 | pcibios_resource_survey(); | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | arch_initcall(pcibios_init); | ||
446 | |||
447 | char * __init pcibios_setup(char *str) | ||
448 | { | ||
449 | if (!strcmp(str, "off")) { | ||
450 | pci_probe = 0; | ||
451 | return NULL; | ||
452 | } else if (!strncmp(str, "lastbus=", 8)) { | ||
453 | pcibios_last_bus = simple_strtol(str+8, NULL, 0); | ||
454 | return NULL; | ||
455 | } | ||
456 | return str; | ||
457 | } | ||
458 | |||
459 | int pcibios_enable_device(struct pci_dev *dev, int mask) | ||
460 | { | ||
461 | int err; | ||
462 | |||
463 | if ((err = pcibios_enable_resources(dev, mask)) < 0) | ||
464 | return err; | ||
465 | pcibios_enable_irq(dev); | ||
466 | return 0; | ||
467 | } | ||