blob: ebb8c8f8f30c4886c28214c9a5eea905c82e9f35 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * 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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/module.h>
19#include <linux/pci.h>
20#include <linux/types.h>
21#include <linux/string.h>
22
23#include <asm/io.h>
24#include <asm/pci-bridge.h>
25#include <asm/machdep.h>
26#include <asm/byteorder.h>
27#include <asm/mpc8260.h>
28#include <asm/immap_cpm2.h>
29#include <asm/cpm2.h>
30
Vitaly Borduga6dbba72005-05-28 15:52:09 -070031#include "m82xx_pci.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33#ifdef CONFIG_8260_PCI9
34/*#include <asm/mpc8260_pci9.h>*/ /* included in asm/io.h */
35
36#define IDMA_XFER_BUF_SIZE 64 /* size of the IDMA transfer buffer */
37
38/* define a structure for the IDMA dpram usage */
39typedef struct idma_dpram_s {
40 idma_t pram; /* IDMA parameter RAM */
41 u_char xfer_buf[IDMA_XFER_BUF_SIZE]; /* IDMA transfer buffer */
42 idma_bd_t bd; /* buffer descriptor */
43} idma_dpram_t;
44
45/* define offsets relative to start of IDMA dpram */
46#define IDMA_XFER_BUF_OFFSET (sizeof(idma_t))
47#define IDMA_BD_OFFSET (sizeof(idma_t) + IDMA_XFER_BUF_SIZE)
48
49/* define globals */
50static volatile idma_dpram_t *idma_dpram;
51
52/* Exactly one of CONFIG_8260_PCI9_IDMAn must be defined,
53 * where n is 1, 2, 3, or 4. This selects the IDMA channel used for
54 * the PCI9 workaround.
55 */
56#ifdef CONFIG_8260_PCI9_IDMA1
57#define IDMA_CHAN 0
58#define PROFF_IDMA PROFF_IDMA1_BASE
59#define IDMA_PAGE CPM_CR_IDMA1_PAGE
60#define IDMA_SBLOCK CPM_CR_IDMA1_SBLOCK
61#endif
62#ifdef CONFIG_8260_PCI9_IDMA2
63#define IDMA_CHAN 1
64#define PROFF_IDMA PROFF_IDMA2_BASE
65#define IDMA_PAGE CPM_CR_IDMA2_PAGE
66#define IDMA_SBLOCK CPM_CR_IDMA2_SBLOCK
67#endif
68#ifdef CONFIG_8260_PCI9_IDMA3
69#define IDMA_CHAN 2
70#define PROFF_IDMA PROFF_IDMA3_BASE
71#define IDMA_PAGE CPM_CR_IDMA3_PAGE
72#define IDMA_SBLOCK CPM_CR_IDMA3_SBLOCK
73#endif
74#ifdef CONFIG_8260_PCI9_IDMA4
75#define IDMA_CHAN 3
76#define PROFF_IDMA PROFF_IDMA4_BASE
77#define IDMA_PAGE CPM_CR_IDMA4_PAGE
78#define IDMA_SBLOCK CPM_CR_IDMA4_SBLOCK
79#endif
80
81void idma_pci9_init(void)
82{
83 uint dpram_offset;
84 volatile idma_t *pram;
85 volatile im_idma_t *idma_reg;
86 volatile cpm2_map_t *immap = cpm2_immr;
87
88 /* allocate IDMA dpram */
89 dpram_offset = cpm_dpalloc(sizeof(idma_dpram_t), 64);
90 idma_dpram = cpm_dpram_addr(dpram_offset);
91
92 /* initialize the IDMA parameter RAM */
93 memset((void *)idma_dpram, 0, sizeof(idma_dpram_t));
94 pram = &idma_dpram->pram;
95 pram->ibase = dpram_offset + IDMA_BD_OFFSET;
96 pram->dpr_buf = dpram_offset + IDMA_XFER_BUF_OFFSET;
97 pram->ss_max = 32;
98 pram->dts = 32;
99
100 /* initialize the IDMA_BASE pointer to the IDMA parameter RAM */
101 *((ushort *) &immap->im_dprambase[PROFF_IDMA]) = dpram_offset;
102
103 /* initialize the IDMA registers */
104 idma_reg = (volatile im_idma_t *) &immap->im_sdma.sdma_idsr1;
105 idma_reg[IDMA_CHAN].idmr = 0; /* mask all IDMA interrupts */
106 idma_reg[IDMA_CHAN].idsr = 0xff; /* clear all event flags */
107
Robert P. J. Dayf8b93a92007-02-05 16:14:04 -0800108 printk(KERN_WARNING
109 "Using IDMA%d for MPC8260 device erratum PCI 9 workaround\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 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 */
125static void
126idma_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 */
177static void
178idma_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 */
223static void
224idma_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}
243EXPORT_SYMBOL(idma_pci9_init);
244EXPORT_SYMBOL(idma_pci9_read);
245EXPORT_SYMBOL(idma_pci9_read_le);
246
247static inline int is_pci_mem(unsigned long addr)
248{
Vitaly Borduga6dbba72005-05-28 15:52:09 -0700249 if (addr >= M82xx_PCI_LOWER_MMIO &&
250 addr <= M82xx_PCI_UPPER_MMIO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 return 1;
Vitaly Borduga6dbba72005-05-28 15:52:09 -0700252 if (addr >= M82xx_PCI_LOWER_MEM &&
253 addr <= M82xx_PCI_UPPER_MEM)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 return 1;
255 return 0;
256}
257
258#define is_pci_mem(pa) ( (pa > 0x80000000) && (pa < 0xc0000000))
259int 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
271int 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
283unsigned 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
295int 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
304int 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
313unsigned 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
322void 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
329void 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
336void 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343void *memcpy_fromio(void *dest, unsigned long src, size_t count)
344{
345 unsigned long pa = iopa((unsigned long) src);
346
347 if (is_pci_mem(pa))
348 idma_pci9_read((u8 *)dest, (u8 *)pa, count, 32, 1);
349 else
350 memcpy(dest, (void *)src, count);
351 return dest;
352}
353
354EXPORT_SYMBOL(readb);
355EXPORT_SYMBOL(readw);
356EXPORT_SYMBOL(readl);
357EXPORT_SYMBOL(inb);
358EXPORT_SYMBOL(inw);
359EXPORT_SYMBOL(inl);
360EXPORT_SYMBOL(insb);
361EXPORT_SYMBOL(insw);
362EXPORT_SYMBOL(insl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363EXPORT_SYMBOL(memcpy_fromio);
364
365#endif /* ifdef CONFIG_8260_PCI9 */
366
367/* Indirect PCI routines adapted from arch/ppc/kernel/indirect_pci.c.
368 * Copyright (C) 1998 Gabriel Paubert.
369 */
370#ifndef CONFIG_8260_PCI9
371#define cfg_read(val, addr, type, op) *val = op((type)(addr))
372#else
373#define cfg_read(val, addr, type, op) \
374 idma_pci9_read_le((u8*)(val),(u8*)(addr),sizeof(*(val)),sizeof(*(val)),0)
375#endif
376
377#define cfg_write(val, addr, type, op) op((type *)(addr), (val))
378
379static int indirect_write_config(struct pci_bus *pbus, unsigned int devfn, int where,
380 int size, u32 value)
381{
382 struct pci_controller *hose = pbus->sysdata;
383 u8 cfg_type = 0;
384 if (ppc_md.pci_exclude_device)
385 if (ppc_md.pci_exclude_device(pbus->number, devfn))
386 return PCIBIOS_DEVICE_NOT_FOUND;
387
388 if (hose->set_cfg_type)
389 if (pbus->number != hose->first_busno)
390 cfg_type = 1;
391
392 out_be32(hose->cfg_addr,
393 (((where & 0xfc) | cfg_type) << 24) | (devfn << 16)
394 | ((pbus->number - hose->bus_offset) << 8) | 0x80);
395
396 switch (size)
397 {
398 case 1:
399 cfg_write(value, hose->cfg_data + (where & 3), u8, out_8);
400 break;
401 case 2:
402 cfg_write(value, hose->cfg_data + (where & 2), u16, out_le16);
403 break;
404 case 4:
405 cfg_write(value, hose->cfg_data + (where & 0), u32, out_le32);
406 break;
407 }
408 return PCIBIOS_SUCCESSFUL;
409}
410
411static int indirect_read_config(struct pci_bus *pbus, unsigned int devfn, int where,
412 int size, u32 *value)
413{
414 struct pci_controller *hose = pbus->sysdata;
415 u8 cfg_type = 0;
416 if (ppc_md.pci_exclude_device)
417 if (ppc_md.pci_exclude_device(pbus->number, devfn))
418 return PCIBIOS_DEVICE_NOT_FOUND;
419
420 if (hose->set_cfg_type)
421 if (pbus->number != hose->first_busno)
422 cfg_type = 1;
423
424 out_be32(hose->cfg_addr,
425 (((where & 0xfc) | cfg_type) << 24) | (devfn << 16)
426 | ((pbus->number - hose->bus_offset) << 8) | 0x80);
427
428 switch (size)
429 {
430 case 1:
431 cfg_read(value, hose->cfg_data + (where & 3), u8 *, in_8);
432 break;
433 case 2:
434 cfg_read(value, hose->cfg_data + (where & 2), u16 *, in_le16);
435 break;
436 case 4:
437 cfg_read(value, hose->cfg_data + (where & 0), u32 *, in_le32);
438 break;
439 }
440 return PCIBIOS_SUCCESSFUL;
441}
442
443static struct pci_ops indirect_pci_ops =
444{
445 .read = indirect_read_config,
446 .write = indirect_write_config,
447};
448
449void
450setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
451{
452 hose->ops = &indirect_pci_ops;
453 hose->cfg_addr = (unsigned int *) ioremap(cfg_addr, 4);
454 hose->cfg_data = (unsigned char *) ioremap(cfg_data, 4);
455}