diff options
Diffstat (limited to 'arch/ppc/syslib/qspan_pci.c')
-rw-r--r-- | arch/ppc/syslib/qspan_pci.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/arch/ppc/syslib/qspan_pci.c b/arch/ppc/syslib/qspan_pci.c new file mode 100644 index 000000000000..57f4ed5e5ae1 --- /dev/null +++ b/arch/ppc/syslib/qspan_pci.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * QSpan pci routines. | ||
3 | * Most 8xx boards use the QSpan PCI bridge. The config address register | ||
4 | * is located 0x500 from the base of the bridge control/status registers. | ||
5 | * The data register is located at 0x504. | ||
6 | * This is a two step operation. First, the address register is written, | ||
7 | * then the data register is read/written as required. | ||
8 | * I don't know what to do about interrupts (yet). | ||
9 | * | ||
10 | * The RPX Classic implementation shares a chip select for normal | ||
11 | * PCI access and QSpan control register addresses. The selection is | ||
12 | * further selected by a bit setting in a board control register. | ||
13 | * Although it should happen, we disable interrupts during this operation | ||
14 | * to make sure some driver doesn't accidentally access the PCI while | ||
15 | * we have switched the chip select. | ||
16 | */ | ||
17 | |||
18 | #include <linux/config.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/init.h> | ||
24 | |||
25 | #include <asm/io.h> | ||
26 | #include <asm/mpc8xx.h> | ||
27 | #include <asm/system.h> | ||
28 | #include <asm/machdep.h> | ||
29 | #include <asm/pci-bridge.h> | ||
30 | |||
31 | |||
32 | /* | ||
33 | * This blows...... | ||
34 | * When reading the configuration space, if something does not respond | ||
35 | * the bus times out and we get a machine check interrupt. So, the | ||
36 | * good ol' exception tables come to mind to trap it and return some | ||
37 | * value. | ||
38 | * | ||
39 | * On an error we just return a -1, since that is what the caller wants | ||
40 | * returned if nothing is present. I copied this from __get_user_asm, | ||
41 | * with the only difference of returning -1 instead of EFAULT. | ||
42 | * There is an associated hack in the machine check trap code. | ||
43 | * | ||
44 | * The QSPAN is also a big endian device, that is it makes the PCI | ||
45 | * look big endian to us. This presents a problem for the Linux PCI | ||
46 | * functions, which assume little endian. For example, we see the | ||
47 | * first 32-bit word like this: | ||
48 | * ------------------------ | ||
49 | * | Device ID | Vendor ID | | ||
50 | * ------------------------ | ||
51 | * If we read/write as a double word, that's OK. But in our world, | ||
52 | * when read as a word, device ID is at location 0, not location 2 as | ||
53 | * the little endian PCI would believe. We have to switch bits in | ||
54 | * the PCI addresses given to us to get the data to/from the correct | ||
55 | * byte lanes. | ||
56 | * | ||
57 | * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5. | ||
58 | * It always forces the MS bit to zero. Therefore, dev_fn values | ||
59 | * greater than 128 are returned as "no device found" errors. | ||
60 | * | ||
61 | * The QSPAN can only perform long word (32-bit) configuration cycles. | ||
62 | * The "offset" must have the two LS bits set to zero. Read operations | ||
63 | * require we read the entire word and then sort out what should be | ||
64 | * returned. Write operations other than long word require that we | ||
65 | * read the long word, update the proper word or byte, then write the | ||
66 | * entire long word back. | ||
67 | * | ||
68 | * PCI Bridge hack. We assume (correctly) that bus 0 is the primary | ||
69 | * PCI bus from the QSPAN. If we are called with a bus number other | ||
70 | * than zero, we create a Type 1 configuration access that a downstream | ||
71 | * PCI bridge will interpret. | ||
72 | */ | ||
73 | |||
74 | #define __get_qspan_pci_config(x, addr, op) \ | ||
75 | __asm__ __volatile__( \ | ||
76 | "1: "op" %0,0(%1)\n" \ | ||
77 | " eieio\n" \ | ||
78 | "2:\n" \ | ||
79 | ".section .fixup,\"ax\"\n" \ | ||
80 | "3: li %0,-1\n" \ | ||
81 | " b 2b\n" \ | ||
82 | ".section __ex_table,\"a\"\n" \ | ||
83 | " .align 2\n" \ | ||
84 | " .long 1b,3b\n" \ | ||
85 | ".text" \ | ||
86 | : "=r"(x) : "r"(addr) : " %0") | ||
87 | |||
88 | #define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500)) | ||
89 | #define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504)) | ||
90 | |||
91 | #define mk_config_addr(bus, dev, offset) \ | ||
92 | (((bus)<<16) | ((dev)<<8) | (offset & 0xfc)) | ||
93 | |||
94 | #define mk_config_type1(bus, dev, offset) \ | ||
95 | mk_config_addr(bus, dev, offset) | 1; | ||
96 | |||
97 | static spinlock_t pcibios_lock = SPIN_LOCK_UNLOCKED; | ||
98 | |||
99 | int qspan_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, | ||
100 | unsigned char offset, unsigned char *val) | ||
101 | { | ||
102 | uint temp; | ||
103 | u_char *cp; | ||
104 | #ifdef CONFIG_RPXCLASSIC | ||
105 | unsigned long flags; | ||
106 | #endif | ||
107 | |||
108 | if ((bus > 7) || (dev_fn > 127)) { | ||
109 | *val = 0xff; | ||
110 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
111 | } | ||
112 | |||
113 | #ifdef CONFIG_RPXCLASSIC | ||
114 | /* disable interrupts */ | ||
115 | spin_lock_irqsave(&pcibios_lock, flags); | ||
116 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
117 | eieio(); | ||
118 | #endif | ||
119 | |||
120 | if (bus == 0) | ||
121 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
122 | else | ||
123 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
124 | __get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz"); | ||
125 | |||
126 | #ifdef CONFIG_RPXCLASSIC | ||
127 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
128 | eieio(); | ||
129 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
130 | #endif | ||
131 | |||
132 | offset ^= 0x03; | ||
133 | cp = ((u_char *)&temp) + (offset & 0x03); | ||
134 | *val = *cp; | ||
135 | return PCIBIOS_SUCCESSFUL; | ||
136 | } | ||
137 | |||
138 | int qspan_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, | ||
139 | unsigned char offset, unsigned short *val) | ||
140 | { | ||
141 | uint temp; | ||
142 | ushort *sp; | ||
143 | #ifdef CONFIG_RPXCLASSIC | ||
144 | unsigned long flags; | ||
145 | #endif | ||
146 | |||
147 | if ((bus > 7) || (dev_fn > 127)) { | ||
148 | *val = 0xffff; | ||
149 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
150 | } | ||
151 | |||
152 | #ifdef CONFIG_RPXCLASSIC | ||
153 | /* disable interrupts */ | ||
154 | spin_lock_irqsave(&pcibios_lock, flags); | ||
155 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
156 | eieio(); | ||
157 | #endif | ||
158 | |||
159 | if (bus == 0) | ||
160 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
161 | else | ||
162 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
163 | __get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz"); | ||
164 | offset ^= 0x02; | ||
165 | |||
166 | #ifdef CONFIG_RPXCLASSIC | ||
167 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
168 | eieio(); | ||
169 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
170 | #endif | ||
171 | |||
172 | sp = ((ushort *)&temp) + ((offset >> 1) & 1); | ||
173 | *val = *sp; | ||
174 | return PCIBIOS_SUCCESSFUL; | ||
175 | } | ||
176 | |||
177 | int qspan_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, | ||
178 | unsigned char offset, unsigned int *val) | ||
179 | { | ||
180 | #ifdef CONFIG_RPXCLASSIC | ||
181 | unsigned long flags; | ||
182 | #endif | ||
183 | |||
184 | if ((bus > 7) || (dev_fn > 127)) { | ||
185 | *val = 0xffffffff; | ||
186 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
187 | } | ||
188 | |||
189 | #ifdef CONFIG_RPXCLASSIC | ||
190 | /* disable interrupts */ | ||
191 | spin_lock_irqsave(&pcibios_lock, flags); | ||
192 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
193 | eieio(); | ||
194 | #endif | ||
195 | |||
196 | if (bus == 0) | ||
197 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
198 | else | ||
199 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
200 | __get_qspan_pci_config(*val, QS_CONFIG_DATA, "lwz"); | ||
201 | |||
202 | #ifdef CONFIG_RPXCLASSIC | ||
203 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
204 | eieio(); | ||
205 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
206 | #endif | ||
207 | |||
208 | return PCIBIOS_SUCCESSFUL; | ||
209 | } | ||
210 | |||
211 | int qspan_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, | ||
212 | unsigned char offset, unsigned char val) | ||
213 | { | ||
214 | uint temp; | ||
215 | u_char *cp; | ||
216 | #ifdef CONFIG_RPXCLASSIC | ||
217 | unsigned long flags; | ||
218 | #endif | ||
219 | |||
220 | if ((bus > 7) || (dev_fn > 127)) | ||
221 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
222 | |||
223 | qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp); | ||
224 | |||
225 | offset ^= 0x03; | ||
226 | cp = ((u_char *)&temp) + (offset & 0x03); | ||
227 | *cp = val; | ||
228 | |||
229 | #ifdef CONFIG_RPXCLASSIC | ||
230 | /* disable interrupts */ | ||
231 | spin_lock_irqsave(&pcibios_lock, flags); | ||
232 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
233 | eieio(); | ||
234 | #endif | ||
235 | |||
236 | if (bus == 0) | ||
237 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
238 | else | ||
239 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
240 | *QS_CONFIG_DATA = temp; | ||
241 | |||
242 | #ifdef CONFIG_RPXCLASSIC | ||
243 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
244 | eieio(); | ||
245 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
246 | #endif | ||
247 | |||
248 | return PCIBIOS_SUCCESSFUL; | ||
249 | } | ||
250 | |||
251 | int qspan_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, | ||
252 | unsigned char offset, unsigned short val) | ||
253 | { | ||
254 | uint temp; | ||
255 | ushort *sp; | ||
256 | #ifdef CONFIG_RPXCLASSIC | ||
257 | unsigned long flags; | ||
258 | #endif | ||
259 | |||
260 | if ((bus > 7) || (dev_fn > 127)) | ||
261 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
262 | |||
263 | qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp); | ||
264 | |||
265 | offset ^= 0x02; | ||
266 | sp = ((ushort *)&temp) + ((offset >> 1) & 1); | ||
267 | *sp = val; | ||
268 | |||
269 | #ifdef CONFIG_RPXCLASSIC | ||
270 | /* disable interrupts */ | ||
271 | spin_lock_irqsave(&pcibios_lock, flags); | ||
272 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
273 | eieio(); | ||
274 | #endif | ||
275 | |||
276 | if (bus == 0) | ||
277 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
278 | else | ||
279 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
280 | *QS_CONFIG_DATA = temp; | ||
281 | |||
282 | #ifdef CONFIG_RPXCLASSIC | ||
283 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
284 | eieio(); | ||
285 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
286 | #endif | ||
287 | |||
288 | return PCIBIOS_SUCCESSFUL; | ||
289 | } | ||
290 | |||
291 | int qspan_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, | ||
292 | unsigned char offset, unsigned int val) | ||
293 | { | ||
294 | #ifdef CONFIG_RPXCLASSIC | ||
295 | unsigned long flags; | ||
296 | #endif | ||
297 | |||
298 | if ((bus > 7) || (dev_fn > 127)) | ||
299 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
300 | |||
301 | #ifdef CONFIG_RPXCLASSIC | ||
302 | /* disable interrupts */ | ||
303 | spin_lock_irqsave(&pcibios_lock, flags); | ||
304 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
305 | eieio(); | ||
306 | #endif | ||
307 | |||
308 | if (bus == 0) | ||
309 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
310 | else | ||
311 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
312 | *(unsigned int *)QS_CONFIG_DATA = val; | ||
313 | |||
314 | #ifdef CONFIG_RPXCLASSIC | ||
315 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
316 | eieio(); | ||
317 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
318 | #endif | ||
319 | |||
320 | return PCIBIOS_SUCCESSFUL; | ||
321 | } | ||
322 | |||
323 | int qspan_pcibios_find_device(unsigned short vendor, unsigned short dev_id, | ||
324 | unsigned short index, unsigned char *bus_ptr, | ||
325 | unsigned char *dev_fn_ptr) | ||
326 | { | ||
327 | int num, devfn; | ||
328 | unsigned int x, vendev; | ||
329 | |||
330 | if (vendor == 0xffff) | ||
331 | return PCIBIOS_BAD_VENDOR_ID; | ||
332 | vendev = (dev_id << 16) + vendor; | ||
333 | num = 0; | ||
334 | for (devfn = 0; devfn < 32; devfn++) { | ||
335 | qspan_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x); | ||
336 | if (x == vendev) { | ||
337 | if (index == num) { | ||
338 | *bus_ptr = 0; | ||
339 | *dev_fn_ptr = devfn<<3; | ||
340 | return PCIBIOS_SUCCESSFUL; | ||
341 | } | ||
342 | ++num; | ||
343 | } | ||
344 | } | ||
345 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
346 | } | ||
347 | |||
348 | int qspan_pcibios_find_class(unsigned int class_code, unsigned short index, | ||
349 | unsigned char *bus_ptr, unsigned char *dev_fn_ptr) | ||
350 | { | ||
351 | int devnr, x, num; | ||
352 | |||
353 | num = 0; | ||
354 | for (devnr = 0; devnr < 32; devnr++) { | ||
355 | qspan_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x); | ||
356 | if ((x>>8) == class_code) { | ||
357 | if (index == num) { | ||
358 | *bus_ptr = 0; | ||
359 | *dev_fn_ptr = devnr<<3; | ||
360 | return PCIBIOS_SUCCESSFUL; | ||
361 | } | ||
362 | ++num; | ||
363 | } | ||
364 | } | ||
365 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
366 | } | ||
367 | |||
368 | void __init | ||
369 | m8xx_pcibios_fixup(void)) | ||
370 | { | ||
371 | /* Lots to do here, all board and configuration specific. */ | ||
372 | } | ||
373 | |||
374 | void __init | ||
375 | m8xx_setup_pci_ptrs(void)) | ||
376 | { | ||
377 | set_config_access_method(qspan); | ||
378 | |||
379 | ppc_md.pcibios_fixup = m8xx_pcibios_fixup; | ||
380 | } | ||
381 | |||