8266 PCI on 2.6.x
David Woodhouse
dwmw2 at infradead.org
Thu Jun 17 23:05:39 EST 2004
On Fri, 2004-06-11 at 16:27 -0500, Rune Torgersen wrote:
> I am trying to get it to work on 2.6.6 and 2.6.7rc3 (both mainstream
> and linuxppc-2.5), but the workaround for PCI errata 9 hangs on
> anything newer that 2.6.5...
The out-of-line read[bwl] functions are doing the IDMA thing even for
non-PCI addresses. I suspect that's probably what causes your problem.
My current 82xx PCI bk tree is at bk://linux-mtd.bkbits.net/new82xx-2.6
Add your board support to it if necessary, and try with this patch:
The workaround really doesn't make me happy though.
diff -Nru a/arch/ppc/Kconfig b/arch/ppc/Kconfig
--- a/arch/ppc/Kconfig 2004-06-17 14:01:00 +01:00
+++ b/arch/ppc/Kconfig 2004-06-17 14:01:00 +01:00
@@ -1026,6 +1026,29 @@
depends on PCI && 8260 && !8272
default y
+config 8260_PCI9
+ bool " Enable workaround for MPC826x erratum PCI 9"
+ depends on PCI_8260
+ default y
+
+choice
+ prompt " IDMA channel for PCI 9 workaround"
+ depends on 8260_PCI9
+
+config 8260_PCI9_IDMA1
+ bool "IDMA1"
+
+config 8260_PCI9_IDMA2
+ bool "IDMA2"
+
+config 8260_PCI9_IDMA3
+ bool "IDMA3"
+
+config 8260_PCI9_IDMA4
+ bool "IDMA4"
+
+endchoice
+
config PCI_PERMEDIA
bool "PCI for Permedia2"
depends on !4xx && !8xx && APUS
diff -Nru a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile
--- a/arch/ppc/syslib/Makefile 2004-06-17 14:01:00 +01:00
+++ b/arch/ppc/syslib/Makefile 2004-06-17 14:01:00 +01:00
@@ -68,6 +68,7 @@
todc_time.o
obj-$(CONFIG_8260) += m8260_setup.o cpm2_pic.o
obj-$(CONFIG_PCI_8260) += m8260_pci.o indirect_pci.o
+obj-$(CONFIG_8260_PCI9) += m8260_pci_erratum9.o
obj-$(CONFIG_CPM2) += cpm2_common.o
ifeq ($(CONFIG_PPC_GEN550),y)
obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o
diff -Nru a/arch/ppc/syslib/m8260_pci.c b/arch/ppc/syslib/m8260_pci.c
--- a/arch/ppc/syslib/m8260_pci.c 2004-06-17 14:01:00 +01:00
+++ b/arch/ppc/syslib/m8260_pci.c 2004-06-17 14:01:00 +01:00
@@ -164,9 +164,9 @@
hose->bus_offset = 0;
hose->last_busno = 0xff;
- setup_indirect_pci(hose,
- (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr,
- (unsigned long)&cpm2_immr->im_pci.pci_cfg_data);
+ setup_m8260_indirect_pci(hose,
+ (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr,
+ (unsigned long)&cpm2_immr->im_pci.pci_cfg_data);
m8260_setup_pci(hose);
hose->pci_mem_offset = MPC826x_PCI_MEM_OFFSET;
diff -Nru a/arch/ppc/syslib/m8260_pci.h b/arch/ppc/syslib/m8260_pci.h
--- a/arch/ppc/syslib/m8260_pci.h 2004-06-17 14:01:00 +01:00
+++ b/arch/ppc/syslib/m8260_pci.h 2004-06-17 14:01:00 +01:00
@@ -65,4 +65,11 @@
#define _IO_BASE isa_io_base
#endif
+#ifdef CONFIG_8260_PCI9
+extern void setup_m8260_indirect_pci(struct pci_controller* hose,
+ u32 cfg_addr, u32 cfg_data);
+#else
+#define setup_m8260_indirect_pci setup_indirect_pci
+#endif
+
#endif /* _PPC_KERNEL_M8260_PCI_H */
diff -Nru a/arch/ppc/syslib/m8260_pci_erratum9.c b/arch/ppc/syslib/m8260_pci_erratum9.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/arch/ppc/syslib/m8260_pci_erratum9.c 2004-06-17 14:01:00 +01:00
@@ -0,0 +1,474 @@
+/*
+ * arch/ppc/platforms/mpc8260_pci9.c
+ *
+ * Workaround for device erratum PCI 9.
+ * See Motorola's "XPC826xA Family Device Errata Reference."
+ * The erratum applies to all 8260 family Hip4 processors. It is scheduled
+ * to be fixed in HiP4 Rev C. Erratum PCI 9 states that a simultaneous PCI
+ * inbound write transaction and PCI outbound read transaction can result in a
+ * bus deadlock. The suggested workaround is to use the IDMA controller to
+ * perform all reads from PCI configuration, memory, and I/O space.
+ *
+ * Author: andy_lowe at mvista.com
+ *
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/byteorder.h>
+#include <asm/mpc8260.h>
+#include <asm/immap_cpm2.h>
+#include <asm/cpm2.h>
+
+#include "m8260_pci.h"
+
+#ifdef CONFIG_8260_PCI9
+/*#include <asm/mpc8260_pci9.h>*/ /* included in asm/io.h */
+
+#define IDMA_XFER_BUF_SIZE 64 /* size of the IDMA transfer buffer */
+
+/* define a structure for the IDMA dpram usage */
+typedef struct idma_dpram_s {
+ idma_t pram; /* IDMA parameter RAM */
+ u_char xfer_buf[IDMA_XFER_BUF_SIZE]; /* IDMA transfer buffer */
+ idma_bd_t bd; /* buffer descriptor */
+} idma_dpram_t;
+
+/* define offsets relative to start of IDMA dpram */
+#define IDMA_XFER_BUF_OFFSET (sizeof(idma_t))
+#define IDMA_BD_OFFSET (sizeof(idma_t) + IDMA_XFER_BUF_SIZE)
+
+/* define globals */
+static volatile idma_dpram_t *idma_dpram;
+
+/* Exactly one of CONFIG_8260_PCI9_IDMAn must be defined,
+ * where n is 1, 2, 3, or 4. This selects the IDMA channel used for
+ * the PCI9 workaround.
+ */
+#ifdef CONFIG_8260_PCI9_IDMA1
+#define IDMA_CHAN 0
+#define PROFF_IDMA PROFF_IDMA1_BASE
+#define IDMA_PAGE CPM_CR_IDMA1_PAGE
+#define IDMA_SBLOCK CPM_CR_IDMA1_SBLOCK
+#endif
+#ifdef CONFIG_8260_PCI9_IDMA2
+#define IDMA_CHAN 1
+#define PROFF_IDMA PROFF_IDMA2_BASE
+#define IDMA_PAGE CPM_CR_IDMA2_PAGE
+#define IDMA_SBLOCK CPM_CR_IDMA2_SBLOCK
+#endif
+#ifdef CONFIG_8260_PCI9_IDMA3
+#define IDMA_CHAN 2
+#define PROFF_IDMA PROFF_IDMA3_BASE
+#define IDMA_PAGE CPM_CR_IDMA3_PAGE
+#define IDMA_SBLOCK CPM_CR_IDMA3_SBLOCK
+#endif
+#ifdef CONFIG_8260_PCI9_IDMA4
+#define IDMA_CHAN 3
+#define PROFF_IDMA PROFF_IDMA4_BASE
+#define IDMA_PAGE CPM_CR_IDMA4_PAGE
+#define IDMA_SBLOCK CPM_CR_IDMA4_SBLOCK
+#endif
+
+void idma_pci9_init(void)
+{
+ uint dpram_offset;
+ volatile idma_t *pram;
+ volatile im_idma_t *idma_reg;
+ volatile cpm2_map_t *immap = cpm2_immr;
+
+ /* allocate IDMA dpram */
+ dpram_offset = cpm2_dpalloc(sizeof(idma_dpram_t), 64);
+ idma_dpram =
+ (volatile idma_dpram_t *)&immap->im_dprambase[dpram_offset];
+
+ /* initialize the IDMA parameter RAM */
+ memset((void *)idma_dpram, 0, sizeof(idma_dpram_t));
+ pram = &idma_dpram->pram;
+ pram->ibase = dpram_offset + IDMA_BD_OFFSET;
+ pram->dpr_buf = dpram_offset + IDMA_XFER_BUF_OFFSET;
+ pram->ss_max = 32;
+ pram->dts = 32;
+
+ /* initialize the IDMA_BASE pointer to the IDMA parameter RAM */
+ *((ushort *) &immap->im_dprambase[PROFF_IDMA]) = dpram_offset;
+
+ /* initialize the IDMA registers */
+ idma_reg = (volatile im_idma_t *) &immap->im_sdma.sdma_idsr1;
+ idma_reg[IDMA_CHAN].idmr = 0; /* mask all IDMA interrupts */
+ idma_reg[IDMA_CHAN].idsr = 0xff; /* clear all event flags */
+
+ printk("<4>Using IDMA%d for MPC8260 device erratum PCI 9 workaround\n",
+ IDMA_CHAN + 1);
+
+ return;
+}
+
+/* Use the IDMA controller to transfer data from I/O memory to local RAM.
+ * The src address must be a physical address suitable for use by the DMA
+ * controller with no translation. The dst address must be a kernel virtual
+ * address. The dst address is translated to a physical address via
+ * virt_to_phys().
+ * The sinc argument specifies whether or not the source address is incremented
+ * by the DMA controller. The source address is incremented if and only if sinc
+ * is non-zero. The destination address is always incremented since the
+ * destination is always host RAM.
+ */
+static void
+idma_pci9_read(u8 *dst, u8 *src, int bytes, int unit_size, int sinc)
+{
+ unsigned long flags;
+ volatile idma_t *pram = &idma_dpram->pram;
+ volatile idma_bd_t *bd = &idma_dpram->bd;
+ volatile cpm2_map_t *immap = cpm2_immr;
+
+ local_irq_save(flags);
+
+ /* initialize IDMA parameter RAM for this transfer */
+ if (sinc)
+ pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
+ | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;
+ else
+ pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_DINC
+ | IDMA_DCM_SD_MEM2MEM;
+ pram->ibdptr = pram->ibase;
+ pram->sts = unit_size;
+ pram->istate = 0;
+
+ /* initialize the buffer descriptor */
+ bd->dst = virt_to_phys(dst);
+ bd->src = (uint) src;
+ bd->len = bytes;
+ bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL
+ | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;
+
+ /* issue the START_IDMA command to the CP */
+ while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
+ immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,
+ CPM_CR_START_IDMA) | CPM_CR_FLG;
+ while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
+
+ /* wait for transfer to complete */
+ while(bd->flags & IDMA_BD_V);
+
+ local_irq_restore(flags);
+
+ return;
+}
+
+/* Use the IDMA controller to transfer data from I/O memory to local RAM.
+ * The dst address must be a physical address suitable for use by the DMA
+ * controller with no translation. The src address must be a kernel virtual
+ * address. The src address is translated to a physical address via
+ * virt_to_phys().
+ * The dinc argument specifies whether or not the dest address is incremented
+ * by the DMA controller. The source address is incremented if and only if sinc
+ * is non-zero. The source address is always incremented since the
+ * source is always host RAM.
+ */
+static void
+idma_pci9_write(u8 *dst, u8 *src, int bytes, int unit_size, int dinc)
+{
+ unsigned long flags;
+ volatile idma_t *pram = &idma_dpram->pram;
+ volatile idma_bd_t *bd = &idma_dpram->bd;
+ volatile cpm2_map_t *immap = cpm2_immr;
+
+ local_irq_save(flags);
+
+ /* initialize IDMA parameter RAM for this transfer */
+ if (dinc)
+ pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
+ | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;
+ else
+ pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
+ | IDMA_DCM_SD_MEM2MEM;
+ pram->ibdptr = pram->ibase;
+ pram->sts = unit_size;
+ pram->istate = 0;
+
+ /* initialize the buffer descriptor */
+ bd->dst = (uint) dst;
+ bd->src = virt_to_phys(src);
+ bd->len = bytes;
+ bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL
+ | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;
+
+ /* issue the START_IDMA command to the CP */
+ while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
+ immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,
+ CPM_CR_START_IDMA) | CPM_CR_FLG;
+ while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
+
+ /* wait for transfer to complete */
+ while(bd->flags & IDMA_BD_V);
+
+ local_irq_restore(flags);
+
+ return;
+}
+
+/* Same as idma_pci9_read, but 16-bit little-endian byte swapping is performed
+ * if the unit_size is 2, and 32-bit little-endian byte swapping is performed if
+ * the unit_size is 4.
+ */
+static void
+idma_pci9_read_le(u8 *dst, u8 *src, int bytes, int unit_size, int sinc)
+{
+ int i;
+ u8 *p;
+
+ idma_pci9_read(dst, src, bytes, unit_size, sinc);
+ switch(unit_size) {
+ case 2:
+ for (i = 0, p = dst; i < bytes; i += 2, p += 2)
+ swab16s((u16 *) p);
+ break;
+ case 4:
+ for (i = 0, p = dst; i < bytes; i += 4, p += 4)
+ swab32s((u32 *) p);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(idma_pci9_init);
+EXPORT_SYMBOL(idma_pci9_read);
+EXPORT_SYMBOL(idma_pci9_read_le);
+
+static inline int is_pci_mem(unsigned long addr)
+{
+ if (addr >= MPC826x_PCI_LOWER_MMIO &&
+ addr <= MPC826x_PCI_UPPER_MMIO)
+ return 1;
+ if (addr >= MPC826x_PCI_LOWER_MEM &&
+ addr <= MPC826x_PCI_UPPER_MEM)
+ return 1;
+ return 0;
+}
+
+#define is_pci_mem(pa) ( (pa > 0x80000000) && (pa < 0xc0000000))
+int readb(volatile unsigned char *addr)
+{
+ u8 val;
+ unsigned long pa = iopa((unsigned long) addr);
+
+ if (!is_pci_mem(pa))
+ return in_8(addr);
+
+ idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
+ return val;
+}
+
+int readw(volatile unsigned short *addr)
+{
+ u16 val;
+ unsigned long pa = iopa((unsigned long) addr);
+
+ if (!is_pci_mem(pa))
+ return in_le16(addr);
+
+ idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
+ return swab16(val);
+}
+
+unsigned readl(volatile unsigned *addr)
+{
+ u32 val;
+ unsigned long pa = iopa((unsigned long) addr);
+
+ if (!is_pci_mem(pa))
+ return in_le32(addr);
+
+ idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
+ return swab32(val);
+}
+
+int inb(unsigned port)
+{
+ u8 val;
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
+ return val;
+}
+
+int inw(unsigned port)
+{
+ u16 val;
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
+ return swab16(val);
+}
+
+unsigned inl(unsigned port)
+{
+ u32 val;
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
+ return swab32(val);
+}
+
+void insb(unsigned port, void *buf, int ns)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u8), sizeof(u8), 0);
+}
+
+void insw(unsigned port, void *buf, int ns)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0);
+}
+
+void insl(unsigned port, void *buf, int nl)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0);
+}
+
+void insw_ns(unsigned port, void *buf, int ns)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0);
+}
+
+void insl_ns(unsigned port, void *buf, int nl)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0);
+}
+
+void *memcpy_fromio(void *dest, unsigned long src, size_t count)
+{
+ unsigned long pa = iopa((unsigned long) src);
+
+ if (is_pci_mem(pa))
+ idma_pci9_read((u8 *)dest, (u8 *)pa, count, 32, 1);
+ else
+ memcpy(dest, (void *)src, count);
+ return dest;
+}
+
+EXPORT_SYMBOL(readb);
+EXPORT_SYMBOL(readw);
+EXPORT_SYMBOL(readl);
+EXPORT_SYMBOL(inb);
+EXPORT_SYMBOL(inw);
+EXPORT_SYMBOL(inl);
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);
+EXPORT_SYMBOL(insw_ns);
+EXPORT_SYMBOL(insl_ns);
+EXPORT_SYMBOL(memcpy_fromio);
+
+#endif /* ifdef CONFIG_8260_PCI9 */
+
+/* Indirect PCI routines adapted from arch/ppc/kernel/indirect_pci.c.
+ * Copyright (C) 1998 Gabriel Paubert.
+ */
+#ifndef CONFIG_8260_PCI9
+#define cfg_read(val, addr, type, op) *val = op((type)(addr))
+#else
+#define cfg_read(val, addr, type, op) \
+ idma_pci9_read_le((u8*)(val),(u8*)(addr),sizeof(*(val)),sizeof(*(val)),0)
+#endif
+
+#define cfg_write(val, addr, type, op) op((type *)(addr), (val))
+
+static int indirect_write_config(struct pci_bus *pbus, unsigned int devfn, int where,
+ int size, u32 value)
+{
+ struct pci_controller *hose = pbus->sysdata;
+ u8 cfg_type = 0;
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(pbus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (hose->set_cfg_type)
+ if (pbus->number != hose->first_busno)
+ cfg_type = 1;
+
+ out_be32(hose->cfg_addr,
+ (((where & 0xfc) | cfg_type) << 24) | (devfn << 16)
+ | ((pbus->number - hose->bus_offset) << 8) | 0x80);
+
+ switch (size)
+ {
+ case 1:
+ cfg_write(value, hose->cfg_data + (where & 3), u8, out_8);
+ break;
+ case 2:
+ cfg_write(value, hose->cfg_data + (where & 2), u16, out_le16);
+ break;
+ case 4:
+ cfg_write(value, hose->cfg_data + (where & 0), u32, out_le32);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int indirect_read_config(struct pci_bus *pbus, unsigned int devfn, int where,
+ int size, u32 *value)
+{
+ struct pci_controller *hose = pbus->sysdata;
+ u8 cfg_type = 0;
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(pbus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (hose->set_cfg_type)
+ if (pbus->number != hose->first_busno)
+ cfg_type = 1;
+
+ out_be32(hose->cfg_addr,
+ (((where & 0xfc) | cfg_type) << 24) | (devfn << 16)
+ | ((pbus->number - hose->bus_offset) << 8) | 0x80);
+
+ switch (size)
+ {
+ case 1:
+ cfg_read(value, hose->cfg_data + (where & 3), u8 *, in_8);
+ break;
+ case 2:
+ cfg_read(value, hose->cfg_data + (where & 2), u16 *, in_le16);
+ break;
+ case 4:
+ cfg_read(value, hose->cfg_data + (where & 0), u32 *, in_le32);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops indirect_pci_ops =
+{
+ .read = indirect_read_config,
+ .write = indirect_write_config,
+};
+
+void
+setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
+{
+ hose->ops = &indirect_pci_ops;
+ hose->cfg_addr = (unsigned int *) ioremap(cfg_addr, 4);
+ hose->cfg_data = (unsigned char *) ioremap(cfg_data, 4);
+}
diff -Nru a/arch/ppc/syslib/m8260_setup.c b/arch/ppc/syslib/m8260_setup.c
--- a/arch/ppc/syslib/m8260_setup.c 2004-06-17 14:01:00 +01:00
+++ b/arch/ppc/syslib/m8260_setup.c 2004-06-17 14:01:00 +01:00
@@ -54,6 +54,7 @@
extern void cpm2_reset(void);
extern void m8260_find_bridges(void);
+extern void idma_pci9_init(void);
static void __init
m8260_setup_arch(void)
@@ -61,6 +62,10 @@
/* Reset the Communication Processor Module.
*/
cpm2_reset();
+#ifdef CONFIG_8260_PCI9
+ /* Initialise IDMA for PCI erratum workaround */
+ idma_pci9_init();
+#endif
#ifdef CONFIG_PCI_8260
m8260_find_bridges();
#endif
diff -Nru a/include/asm-ppc/io.h b/include/asm-ppc/io.h
--- a/include/asm-ppc/io.h 2004-06-17 14:01:00 +01:00
+++ b/include/asm-ppc/io.h 2004-06-17 14:01:00 +01:00
@@ -138,18 +138,27 @@
: : "r" (val), "r" (port + _IO_BASE)); \
}
-__do_in_asm(inb, "lbzx")
__do_out_asm(outb, "stbx")
#ifdef CONFIG_APUS
+__do_in_asm(inb, "lbzx")
__do_in_asm(inw, "lhz%U1%X1")
__do_in_asm(inl, "lwz%U1%X1")
__do_out_asm(outl,"stw%U0%X0")
__do_out_asm(outw, "sth%U0%X0")
+#elif defined (CONFIG_8260_PCI9)
+/* in asm cannot be defined if PCI9 workaround is used */
+#define inb(port) in_8((u8 *)((port)+_IO_BASE))
+#define inw(port) in_le16((u16 *)((port)+_IO_BASE))
+#define inl(port) in_le32((u32 *)((port)+_IO_BASE))
+__do_out_asm(outw, "sthbrx")
+__do_out_asm(outl, "stwbrx")
#else
+__do_in_asm(inb, "lbzx")
__do_in_asm(inw, "lhbrx")
__do_in_asm(inl, "lwbrx")
__do_out_asm(outw, "sthbrx")
__do_out_asm(outl, "stwbrx")
+
#endif
#define inb_p(port) inb((port))
@@ -389,4 +398,9 @@
}
#endif /* _PPC_IO_H */
+
+#ifdef CONFIG_8260_PCI9
+#include <asm/mpc8260_pci9.h>
+#endif
+
#endif /* __KERNEL__ */
diff -Nru a/include/asm-ppc/mpc8260_pci9.h b/include/asm-ppc/mpc8260_pci9.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/include/asm-ppc/mpc8260_pci9.h 2004-06-17 14:01:00 +01:00
@@ -0,0 +1,51 @@
+/* include/asm-ppc/mpc8260_pci9.h
+ *
+ * Undefine the PCI read* and in* macros so we can define them as functions
+ * that implement the workaround for the MPC8260 device erratum PCI 9.
+ *
+ * This header file should only be included at the end of include/asm-ppc/io.h
+ * and never included directly anywhere else.
+ *
+ * Author: andy_lowe at mvista.com
+ *
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#ifndef _PPC_IO_H
+#error "Do not include mpc8260_pci9.h directly."
+#endif
+
+#ifdef __KERNEL__
+#ifndef __CONFIG_8260_PCI9_DEFS
+#define __CONFIG_8260_PCI9_DEFS
+
+#undef readb
+#undef readw
+#undef readl
+#undef insb
+#undef insw
+#undef insl
+#undef inb
+#undef inw
+#undef inl
+#undef insw_ns
+#undef insl_ns
+#undef memcpy_fromio
+
+extern int readb(volatile unsigned char *addr);
+extern int readw(volatile unsigned short *addr);
+extern unsigned readl(volatile unsigned *addr);
+extern void insb(unsigned port, void *buf, int ns);
+extern void insw(unsigned port, void *buf, int ns);
+extern void insl(unsigned port, void *buf, int nl);
+extern int inb(unsigned port);
+extern int inw(unsigned port);
+extern unsigned inl(unsigned port);
+extern void insw_ns(unsigned port, void *buf, int ns);
+extern void insl_ns(unsigned port, void *buf, int nl);
+extern void *memcpy_fromio(void *dest, unsigned long src, size_t count);
+
+#endif /* !__CONFIG_8260_PCI9_DEFS */
+#endif /* __KERNEL__ */
)
> Below is the function (uses IDMA to read PCI config registers)
> This function gets called when a readb, readw or readl is issued.
>
> On 2.6.6 & 7 it hangs on the line
> while(bd->flags & IDMA_BD_V);
>
> Problem is that this function works when the kernel is scanning the PCI
> bus, and initializing the IDE controller.
> It only hangs right before starting init.
>
> What changed between 2.6.5 and 2.6.6? I am doing diffs, and trying to
> see what changed that coud affect this, but have no clue what I'm
> looking for.
>
>
> /* Use the IDMA controller to transfer data from I/O memory to local
> RAM.
> * The src address must be a physical address suitable for use by the
> DMA
> * controller with no translation. The dst address must be a kernel
> virtual
> * address. The dst address is translated to a physical address via
> * virt_to_phys().
> * The sinc argument specifies whether or not the source address is
> incremented
> * by the DMA controller. The source address is incremented if and only
> if sinc
> * is non-zero. The destination address is always incremented since the
>
> * destination is always host RAM.
> */
> static void
> idma_pci9_read(u8 *dst, u8 *src, int bytes, int unit_size, int sinc)
> {
> unsigned long flags;
> volatile idma_t *pram = &idma_dpram->pram;
> volatile idma_bd_t *bd = &idma_dpram->bd;
> volatile immap_t *immap = (immap_t *)IMAP_ADDR;
>
> local_irq_save(flags);
>
> /* initialize IDMA parameter RAM for this transfer */
> if (sinc)
> pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
> | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;
> else
> pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_DINC
> | IDMA_DCM_SD_MEM2MEM;
> pram->ibdptr = pram->ibase;
> pram->sts = unit_size;
> pram->istate = 0;
>
> /* initialize the buffer descriptor */
> bd->dst = virt_to_phys(dst);
> bd->src = (uint) src;
> bd->len = bytes;
> bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L |
> IDMA_BD_DGBL
> | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;
>
> /* issue the START_IDMA command to the CP */
> while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
> immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,
> CPM_CR_START_IDMA) |
> CPM_CR_FLG;
> while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
>
> /* wait for transfer to complete */
> while(bd->flags & IDMA_BD_V);
>
> local_irq_restore(flags);
>
> return;
> }
>
>
>
> Linux version 2.6.6 (runet at ernie.innovsys.com) (gcc version 3.2.2
> 20030217 (Yellow Dog Linux 3.0 3.2.2-2a_1)) #7 Fri Jun 11 16:12:24
> Innovative Systems LLC AP2 port
> Using IDMA4 for MPC8260 device erratum PCI 9 workaround
> On node 0 totalpages: 65536
> DMA zone: 65536 pages, LIFO batch:16
> Normal zone: 0 pages, LIFO batch:1
> HighMem zone: 0 pages, LIFO batch:1
> Built 1 zonelists
> Kernel command line: console=ttyS0,115200 root=/dev/hda3 rw
> ip=172.23.11.125:172.23.14.39:172.23.8.150:255.255.248.0:gold4_cpu1:ethe
> PID hash table entries: 2048 (order 11: 16384 bytes)
> Warning: real time clock seems stuck!
> Memory: 257280k available (1664k kernel code, 436k data, 248k init, 0k
> highmem)
> Calibrating delay loop... 192.00 BogoMIPS
> Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
> Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
> Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
> POSIX conformance testing by UNIFIX
> NET: Registered protocol family 16
> PCI: Probing PCI hardware
> Installing knfsd (copyright (C) 1996 okir at monad.swb.de).
> Uniform Multi-Platform E-IDE driver Revision: 7.00alpha2
> ide: Assuming 33MHz system bus speed for PIO modes; override with
> idebus=xx
> SiI680: IDE controller at PCI slot 0000:00:11.0
> SiI680: chipset revision 2
> SiI680: BASE CLOCK == 133
> SiI680: 100% native mode on irq 69
> ide0: MMIO-DMA , BIOS settings: hda:pio, hdb:pio
> ide1: MMIO-DMA , BIOS settings: hdc:pio, hdd:pio
> Probing IDE interface ide0...
> hda: Maxtor 5A250J0, ATA DISK drive
> Using anticipatory io scheduler
> ide0 at 0xd1000f80-0xd1000f87,0xd1000f8a on irq 69
> Probing IDE interface ide1...
> hda: max request size: 64KiB
> hda: 490234752 sectors (251000 MB) w/2048KiB Cache, CHS=30515/255/63,
> UDMA(133)
> hda: hda1 hda2 hda3 hda4
> CPM UART driver version 0.02
> ttyS0 on SMC1 at 0x0000, BRG7
> ttyS1 on SCC1 at 0x0040, BRG8
> eth0: FCC ENET Version 0.3, 00:30:d7:00:01:09
> eth1: FCC ENET Version 0.3, 00:30:d7:00:01:0a
> NET: Registered protocol family 2
> IP: routing cache hash table of 2048 buckets, 16Kbytes
> TCP: Hash tables configured (established 16384 bind 32768)
> NET: Registered protocol family 1
> NET: Registered protocol family 17
> IP-Config: Complete:
> device=eth1, addr=172.23.11.125, mask=255.255.248.0,
> gw=172.23.8.150,
> host=gold4_cpu1, domain=, nis-domain=(none),
> bootserver=172.23.14.39, rootserver=172.23.14.39, rootpath=
> Rune Torgersen
> System Developer
> Innovative Systems LLC
> 1000 Innovative Drive
> Mitchell, SD 57301
> Ph: 605-995-6120
> www.innovsys.com
>
--
dwmw2
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
More information about the Linuxppc-embedded
mailing list