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