Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * Workaround for device erratum PCI 9. |
3 | * See Motorola's "XPC826xA Family Device Errata Reference." | |
4 | * The erratum applies to all 8260 family Hip4 processors. It is scheduled | |
5 | * to be fixed in HiP4 Rev C. Erratum PCI 9 states that a simultaneous PCI | |
6 | * inbound write transaction and PCI outbound read transaction can result in a | |
7 | * bus deadlock. The suggested workaround is to use the IDMA controller to | |
8 | * perform all reads from PCI configuration, memory, and I/O space. | |
9 | * | |
10 | * Author: andy_lowe@mvista.com | |
11 | * | |
12 | * 2003 (c) MontaVista Software, Inc. This file is licensed under | |
13 | * the terms of the GNU General Public License version 2. This program | |
14 | * is licensed "as is" without any warranty of any kind, whether express | |
15 | * or implied. | |
16 | */ | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/config.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/pci.h> | |
21 | #include <linux/types.h> | |
22 | #include <linux/string.h> | |
23 | ||
24 | #include <asm/io.h> | |
25 | #include <asm/pci-bridge.h> | |
26 | #include <asm/machdep.h> | |
27 | #include <asm/byteorder.h> | |
28 | #include <asm/mpc8260.h> | |
29 | #include <asm/immap_cpm2.h> | |
30 | #include <asm/cpm2.h> | |
31 | ||
a6dbba77 | 32 | #include "m82xx_pci.h" |
1da177e4 LT |
33 | |
34 | #ifdef CONFIG_8260_PCI9 | |
35 | /*#include <asm/mpc8260_pci9.h>*/ /* included in asm/io.h */ | |
36 | ||
37 | #define IDMA_XFER_BUF_SIZE 64 /* size of the IDMA transfer buffer */ | |
38 | ||
39 | /* define a structure for the IDMA dpram usage */ | |
40 | typedef struct idma_dpram_s { | |
41 | idma_t pram; /* IDMA parameter RAM */ | |
42 | u_char xfer_buf[IDMA_XFER_BUF_SIZE]; /* IDMA transfer buffer */ | |
43 | idma_bd_t bd; /* buffer descriptor */ | |
44 | } idma_dpram_t; | |
45 | ||
46 | /* define offsets relative to start of IDMA dpram */ | |
47 | #define IDMA_XFER_BUF_OFFSET (sizeof(idma_t)) | |
48 | #define IDMA_BD_OFFSET (sizeof(idma_t) + IDMA_XFER_BUF_SIZE) | |
49 | ||
50 | /* define globals */ | |
51 | static volatile idma_dpram_t *idma_dpram; | |
52 | ||
53 | /* Exactly one of CONFIG_8260_PCI9_IDMAn must be defined, | |
54 | * where n is 1, 2, 3, or 4. This selects the IDMA channel used for | |
55 | * the PCI9 workaround. | |
56 | */ | |
57 | #ifdef CONFIG_8260_PCI9_IDMA1 | |
58 | #define IDMA_CHAN 0 | |
59 | #define PROFF_IDMA PROFF_IDMA1_BASE | |
60 | #define IDMA_PAGE CPM_CR_IDMA1_PAGE | |
61 | #define IDMA_SBLOCK CPM_CR_IDMA1_SBLOCK | |
62 | #endif | |
63 | #ifdef CONFIG_8260_PCI9_IDMA2 | |
64 | #define IDMA_CHAN 1 | |
65 | #define PROFF_IDMA PROFF_IDMA2_BASE | |
66 | #define IDMA_PAGE CPM_CR_IDMA2_PAGE | |
67 | #define IDMA_SBLOCK CPM_CR_IDMA2_SBLOCK | |
68 | #endif | |
69 | #ifdef CONFIG_8260_PCI9_IDMA3 | |
70 | #define IDMA_CHAN 2 | |
71 | #define PROFF_IDMA PROFF_IDMA3_BASE | |
72 | #define IDMA_PAGE CPM_CR_IDMA3_PAGE | |
73 | #define IDMA_SBLOCK CPM_CR_IDMA3_SBLOCK | |
74 | #endif | |
75 | #ifdef CONFIG_8260_PCI9_IDMA4 | |
76 | #define IDMA_CHAN 3 | |
77 | #define PROFF_IDMA PROFF_IDMA4_BASE | |
78 | #define IDMA_PAGE CPM_CR_IDMA4_PAGE | |
79 | #define IDMA_SBLOCK CPM_CR_IDMA4_SBLOCK | |
80 | #endif | |
81 | ||
82 | void idma_pci9_init(void) | |
83 | { | |
84 | uint dpram_offset; | |
85 | volatile idma_t *pram; | |
86 | volatile im_idma_t *idma_reg; | |
87 | volatile cpm2_map_t *immap = cpm2_immr; | |
88 | ||
89 | /* allocate IDMA dpram */ | |
90 | dpram_offset = cpm_dpalloc(sizeof(idma_dpram_t), 64); | |
91 | idma_dpram = cpm_dpram_addr(dpram_offset); | |
92 | ||
93 | /* initialize the IDMA parameter RAM */ | |
94 | memset((void *)idma_dpram, 0, sizeof(idma_dpram_t)); | |
95 | pram = &idma_dpram->pram; | |
96 | pram->ibase = dpram_offset + IDMA_BD_OFFSET; | |
97 | pram->dpr_buf = dpram_offset + IDMA_XFER_BUF_OFFSET; | |
98 | pram->ss_max = 32; | |
99 | pram->dts = 32; | |
100 | ||
101 | /* initialize the IDMA_BASE pointer to the IDMA parameter RAM */ | |
102 | *((ushort *) &immap->im_dprambase[PROFF_IDMA]) = dpram_offset; | |
103 | ||
104 | /* initialize the IDMA registers */ | |
105 | idma_reg = (volatile im_idma_t *) &immap->im_sdma.sdma_idsr1; | |
106 | idma_reg[IDMA_CHAN].idmr = 0; /* mask all IDMA interrupts */ | |
107 | idma_reg[IDMA_CHAN].idsr = 0xff; /* clear all event flags */ | |
108 | ||
109 | printk("<4>Using IDMA%d for MPC8260 device erratum PCI 9 workaround\n", | |
110 | IDMA_CHAN + 1); | |
111 | ||
112 | return; | |
113 | } | |
114 | ||
115 | /* Use the IDMA controller to transfer data from I/O memory to local RAM. | |
116 | * The src address must be a physical address suitable for use by the DMA | |
117 | * controller with no translation. The dst address must be a kernel virtual | |
118 | * address. The dst address is translated to a physical address via | |
119 | * virt_to_phys(). | |
120 | * The sinc argument specifies whether or not the source address is incremented | |
121 | * by the DMA controller. The source address is incremented if and only if sinc | |
122 | * is non-zero. The destination address is always incremented since the | |
123 | * destination is always host RAM. | |
124 | */ | |
125 | static void | |
126 | idma_pci9_read(u8 *dst, u8 *src, int bytes, int unit_size, int sinc) | |
127 | { | |
128 | unsigned long flags; | |
129 | volatile idma_t *pram = &idma_dpram->pram; | |
130 | volatile idma_bd_t *bd = &idma_dpram->bd; | |
131 | volatile cpm2_map_t *immap = cpm2_immr; | |
132 | ||
133 | local_irq_save(flags); | |
134 | ||
135 | /* initialize IDMA parameter RAM for this transfer */ | |
136 | if (sinc) | |
137 | pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC | |
138 | | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM; | |
139 | else | |
140 | pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_DINC | |
141 | | IDMA_DCM_SD_MEM2MEM; | |
142 | pram->ibdptr = pram->ibase; | |
143 | pram->sts = unit_size; | |
144 | pram->istate = 0; | |
145 | ||
146 | /* initialize the buffer descriptor */ | |
147 | bd->dst = virt_to_phys(dst); | |
148 | bd->src = (uint) src; | |
149 | bd->len = bytes; | |
150 | bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL | |
151 | | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB; | |
152 | ||
153 | /* issue the START_IDMA command to the CP */ | |
154 | while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); | |
155 | immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0, | |
156 | CPM_CR_START_IDMA) | CPM_CR_FLG; | |
157 | while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); | |
158 | ||
159 | /* wait for transfer to complete */ | |
160 | while(bd->flags & IDMA_BD_V); | |
161 | ||
162 | local_irq_restore(flags); | |
163 | ||
164 | return; | |
165 | } | |
166 | ||
167 | /* Use the IDMA controller to transfer data from I/O memory to local RAM. | |
168 | * The dst address must be a physical address suitable for use by the DMA | |
169 | * controller with no translation. The src address must be a kernel virtual | |
170 | * address. The src address is translated to a physical address via | |
171 | * virt_to_phys(). | |
172 | * The dinc argument specifies whether or not the dest address is incremented | |
173 | * by the DMA controller. The source address is incremented if and only if sinc | |
174 | * is non-zero. The source address is always incremented since the | |
175 | * source is always host RAM. | |
176 | */ | |
177 | static void | |
178 | idma_pci9_write(u8 *dst, u8 *src, int bytes, int unit_size, int dinc) | |
179 | { | |
180 | unsigned long flags; | |
181 | volatile idma_t *pram = &idma_dpram->pram; | |
182 | volatile idma_bd_t *bd = &idma_dpram->bd; | |
183 | volatile cpm2_map_t *immap = cpm2_immr; | |
184 | ||
185 | local_irq_save(flags); | |
186 | ||
187 | /* initialize IDMA parameter RAM for this transfer */ | |
188 | if (dinc) | |
189 | pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC | |
190 | | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM; | |
191 | else | |
192 | pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC | |
193 | | IDMA_DCM_SD_MEM2MEM; | |
194 | pram->ibdptr = pram->ibase; | |
195 | pram->sts = unit_size; | |
196 | pram->istate = 0; | |
197 | ||
198 | /* initialize the buffer descriptor */ | |
199 | bd->dst = (uint) dst; | |
200 | bd->src = virt_to_phys(src); | |
201 | bd->len = bytes; | |
202 | bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL | |
203 | | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB; | |
204 | ||
205 | /* issue the START_IDMA command to the CP */ | |
206 | while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); | |
207 | immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0, | |
208 | CPM_CR_START_IDMA) | CPM_CR_FLG; | |
209 | while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); | |
210 | ||
211 | /* wait for transfer to complete */ | |
212 | while(bd->flags & IDMA_BD_V); | |
213 | ||
214 | local_irq_restore(flags); | |
215 | ||
216 | return; | |
217 | } | |
218 | ||
219 | /* Same as idma_pci9_read, but 16-bit little-endian byte swapping is performed | |
220 | * if the unit_size is 2, and 32-bit little-endian byte swapping is performed if | |
221 | * the unit_size is 4. | |
222 | */ | |
223 | static void | |
224 | idma_pci9_read_le(u8 *dst, u8 *src, int bytes, int unit_size, int sinc) | |
225 | { | |
226 | int i; | |
227 | u8 *p; | |
228 | ||
229 | idma_pci9_read(dst, src, bytes, unit_size, sinc); | |
230 | switch(unit_size) { | |
231 | case 2: | |
232 | for (i = 0, p = dst; i < bytes; i += 2, p += 2) | |
233 | swab16s((u16 *) p); | |
234 | break; | |
235 | case 4: | |
236 | for (i = 0, p = dst; i < bytes; i += 4, p += 4) | |
237 | swab32s((u32 *) p); | |
238 | break; | |
239 | default: | |
240 | break; | |
241 | } | |
242 | } | |
243 | EXPORT_SYMBOL(idma_pci9_init); | |
244 | EXPORT_SYMBOL(idma_pci9_read); | |
245 | EXPORT_SYMBOL(idma_pci9_read_le); | |
246 | ||
247 | static inline int is_pci_mem(unsigned long addr) | |
248 | { | |
a6dbba77 VB |
249 | if (addr >= M82xx_PCI_LOWER_MMIO && |
250 | addr <= M82xx_PCI_UPPER_MMIO) | |
1da177e4 | 251 | return 1; |
a6dbba77 VB |
252 | if (addr >= M82xx_PCI_LOWER_MEM && |
253 | addr <= M82xx_PCI_UPPER_MEM) | |
1da177e4 LT |
254 | return 1; |
255 | return 0; | |
256 | } | |
257 | ||
258 | #define is_pci_mem(pa) ( (pa > 0x80000000) && (pa < 0xc0000000)) | |
259 | int readb(volatile unsigned char *addr) | |
260 | { | |
261 | u8 val; | |
262 | unsigned long pa = iopa((unsigned long) addr); | |
263 | ||
264 | if (!is_pci_mem(pa)) | |
265 | return in_8(addr); | |
266 | ||
267 | idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); | |
268 | return val; | |
269 | } | |
270 | ||
271 | int readw(volatile unsigned short *addr) | |
272 | { | |
273 | u16 val; | |
274 | unsigned long pa = iopa((unsigned long) addr); | |
275 | ||
276 | if (!is_pci_mem(pa)) | |
277 | return in_le16(addr); | |
278 | ||
279 | idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); | |
280 | return swab16(val); | |
281 | } | |
282 | ||
283 | unsigned readl(volatile unsigned *addr) | |
284 | { | |
285 | u32 val; | |
286 | unsigned long pa = iopa((unsigned long) addr); | |
287 | ||
288 | if (!is_pci_mem(pa)) | |
289 | return in_le32(addr); | |
290 | ||
291 | idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); | |
292 | return swab32(val); | |
293 | } | |
294 | ||
295 | int inb(unsigned port) | |
296 | { | |
297 | u8 val; | |
298 | u8 *addr = (u8 *)(port + _IO_BASE); | |
299 | ||
300 | idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); | |
301 | return val; | |
302 | } | |
303 | ||
304 | int inw(unsigned port) | |
305 | { | |
306 | u16 val; | |
307 | u8 *addr = (u8 *)(port + _IO_BASE); | |
308 | ||
309 | idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); | |
310 | return swab16(val); | |
311 | } | |
312 | ||
313 | unsigned inl(unsigned port) | |
314 | { | |
315 | u32 val; | |
316 | u8 *addr = (u8 *)(port + _IO_BASE); | |
317 | ||
318 | idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); | |
319 | return swab32(val); | |
320 | } | |
321 | ||
322 | void insb(unsigned port, void *buf, int ns) | |
323 | { | |
324 | u8 *addr = (u8 *)(port + _IO_BASE); | |
325 | ||
326 | idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u8), sizeof(u8), 0); | |
327 | } | |
328 | ||
329 | void insw(unsigned port, void *buf, int ns) | |
330 | { | |
331 | u8 *addr = (u8 *)(port + _IO_BASE); | |
332 | ||
333 | idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0); | |
334 | } | |
335 | ||
336 | void insl(unsigned port, void *buf, int nl) | |
337 | { | |
338 | u8 *addr = (u8 *)(port + _IO_BASE); | |
339 | ||
340 | idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0); | |
341 | } | |
342 | ||
343 | void insw_ns(unsigned port, void *buf, int ns) | |
344 | { | |
345 | u8 *addr = (u8 *)(port + _IO_BASE); | |
346 | ||
347 | idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0); | |
348 | } | |
349 | ||
350 | void insl_ns(unsigned port, void *buf, int nl) | |
351 | { | |
352 | u8 *addr = (u8 *)(port + _IO_BASE); | |
353 | ||
354 | idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0); | |
355 | } | |
356 | ||
357 | void *memcpy_fromio(void *dest, unsigned long src, size_t count) | |
358 | { | |
359 | unsigned long pa = iopa((unsigned long) src); | |
360 | ||
361 | if (is_pci_mem(pa)) | |
362 | idma_pci9_read((u8 *)dest, (u8 *)pa, count, 32, 1); | |
363 | else | |
364 | memcpy(dest, (void *)src, count); | |
365 | return dest; | |
366 | } | |
367 | ||
368 | EXPORT_SYMBOL(readb); | |
369 | EXPORT_SYMBOL(readw); | |
370 | EXPORT_SYMBOL(readl); | |
371 | EXPORT_SYMBOL(inb); | |
372 | EXPORT_SYMBOL(inw); | |
373 | EXPORT_SYMBOL(inl); | |
374 | EXPORT_SYMBOL(insb); | |
375 | EXPORT_SYMBOL(insw); | |
376 | EXPORT_SYMBOL(insl); | |
377 | EXPORT_SYMBOL(insw_ns); | |
378 | EXPORT_SYMBOL(insl_ns); | |
379 | EXPORT_SYMBOL(memcpy_fromio); | |
380 | ||
381 | #endif /* ifdef CONFIG_8260_PCI9 */ | |
382 | ||
383 | /* Indirect PCI routines adapted from arch/ppc/kernel/indirect_pci.c. | |
384 | * Copyright (C) 1998 Gabriel Paubert. | |
385 | */ | |
386 | #ifndef CONFIG_8260_PCI9 | |
387 | #define cfg_read(val, addr, type, op) *val = op((type)(addr)) | |
388 | #else | |
389 | #define cfg_read(val, addr, type, op) \ | |
390 | idma_pci9_read_le((u8*)(val),(u8*)(addr),sizeof(*(val)),sizeof(*(val)),0) | |
391 | #endif | |
392 | ||
393 | #define cfg_write(val, addr, type, op) op((type *)(addr), (val)) | |
394 | ||
395 | static int indirect_write_config(struct pci_bus *pbus, unsigned int devfn, int where, | |
396 | int size, u32 value) | |
397 | { | |
398 | struct pci_controller *hose = pbus->sysdata; | |
399 | u8 cfg_type = 0; | |
400 | if (ppc_md.pci_exclude_device) | |
401 | if (ppc_md.pci_exclude_device(pbus->number, devfn)) | |
402 | return PCIBIOS_DEVICE_NOT_FOUND; | |
403 | ||
404 | if (hose->set_cfg_type) | |
405 | if (pbus->number != hose->first_busno) | |
406 | cfg_type = 1; | |
407 | ||
408 | out_be32(hose->cfg_addr, | |
409 | (((where & 0xfc) | cfg_type) << 24) | (devfn << 16) | |
410 | | ((pbus->number - hose->bus_offset) << 8) | 0x80); | |
411 | ||
412 | switch (size) | |
413 | { | |
414 | case 1: | |
415 | cfg_write(value, hose->cfg_data + (where & 3), u8, out_8); | |
416 | break; | |
417 | case 2: | |
418 | cfg_write(value, hose->cfg_data + (where & 2), u16, out_le16); | |
419 | break; | |
420 | case 4: | |
421 | cfg_write(value, hose->cfg_data + (where & 0), u32, out_le32); | |
422 | break; | |
423 | } | |
424 | return PCIBIOS_SUCCESSFUL; | |
425 | } | |
426 | ||
427 | static int indirect_read_config(struct pci_bus *pbus, unsigned int devfn, int where, | |
428 | int size, u32 *value) | |
429 | { | |
430 | struct pci_controller *hose = pbus->sysdata; | |
431 | u8 cfg_type = 0; | |
432 | if (ppc_md.pci_exclude_device) | |
433 | if (ppc_md.pci_exclude_device(pbus->number, devfn)) | |
434 | return PCIBIOS_DEVICE_NOT_FOUND; | |
435 | ||
436 | if (hose->set_cfg_type) | |
437 | if (pbus->number != hose->first_busno) | |
438 | cfg_type = 1; | |
439 | ||
440 | out_be32(hose->cfg_addr, | |
441 | (((where & 0xfc) | cfg_type) << 24) | (devfn << 16) | |
442 | | ((pbus->number - hose->bus_offset) << 8) | 0x80); | |
443 | ||
444 | switch (size) | |
445 | { | |
446 | case 1: | |
447 | cfg_read(value, hose->cfg_data + (where & 3), u8 *, in_8); | |
448 | break; | |
449 | case 2: | |
450 | cfg_read(value, hose->cfg_data + (where & 2), u16 *, in_le16); | |
451 | break; | |
452 | case 4: | |
453 | cfg_read(value, hose->cfg_data + (where & 0), u32 *, in_le32); | |
454 | break; | |
455 | } | |
456 | return PCIBIOS_SUCCESSFUL; | |
457 | } | |
458 | ||
459 | static struct pci_ops indirect_pci_ops = | |
460 | { | |
461 | .read = indirect_read_config, | |
462 | .write = indirect_write_config, | |
463 | }; | |
464 | ||
465 | void | |
466 | setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) | |
467 | { | |
468 | hose->ops = &indirect_pci_ops; | |
469 | hose->cfg_addr = (unsigned int *) ioremap(cfg_addr, 4); | |
470 | hose->cfg_data = (unsigned char *) ioremap(cfg_data, 4); | |
471 | } |