Initial revision
authorMarc Fiuczynski <mef@cs.princeton.edu>
Fri, 23 Jul 2004 15:20:08 +0000 (15:20 +0000)
committerMarc Fiuczynski <mef@cs.princeton.edu>
Fri, 23 Jul 2004 15:20:08 +0000 (15:20 +0000)
35 files changed:
arch/ppc/boot/simple/misc-mv64x60.S [new file with mode: 0644]
arch/ppc/boot/simple/mv64x60_stub.c [new file with mode: 0644]
arch/ppc/boot/simple/mv64x60_tty.c [new file with mode: 0644]
arch/ppc/platforms/dmv182.c [new file with mode: 0644]
arch/ppc/platforms/dmv182.h [new file with mode: 0644]
arch/ppc/platforms/dmv182_serial.h [new file with mode: 0644]
arch/ppc/platforms/ev64260.c [new file with mode: 0644]
arch/ppc/syslib/mv64360_pic.c [new file with mode: 0644]
arch/ppc/syslib/mv64x60.c [new file with mode: 0644]
arch/ppc/syslib/mv64x60_ocp.c [new file with mode: 0644]
drivers/mtd/chips/cfi_util.c [new file with mode: 0644]
drivers/mtd/devices/phram.c [new file with mode: 0644]
drivers/mtd/maps/ichxrom.c [new file with mode: 0644]
drivers/mtd/nand/diskonchip.c [new file with mode: 0644]
drivers/mtd/nand/nand_base.c [new file with mode: 0644]
drivers/mtd/nand/tx4925ndfmc.c [new file with mode: 0644]
drivers/net/gianfar.c [new file with mode: 0644]
drivers/net/gianfar.h [new file with mode: 0644]
drivers/net/gianfar_ethtool.c [new file with mode: 0644]
drivers/net/gianfar_phy.c [new file with mode: 0644]
drivers/net/gianfar_phy.h [new file with mode: 0644]
drivers/serial/mpsc/Makefile [new file with mode: 0644]
drivers/serial/mpsc/mpsc.c [new file with mode: 0644]
drivers/serial/mpsc/mpsc.h [new file with mode: 0644]
drivers/serial/mpsc/mpsc_defs.h [new file with mode: 0644]
drivers/serial/mpsc/mpsc_ppc32.c [new file with mode: 0644]
fs/jffs2/compr.h [new file with mode: 0644]
fs/jffs2/proc.c [new file with mode: 0644]
include/asm-ppc/mv64x60.h [new file with mode: 0644]
include/asm-ppc/mv64x60_defs.h [new file with mode: 0644]
include/linux/mtd/physmap.h [new file with mode: 0644]
include/mtd/inftl-user.h [new file with mode: 0644]
include/mtd/jffs2-user.h [new file with mode: 0644]
include/mtd/mtd-abi.h [new file with mode: 0644]
include/mtd/nftl-user.h [new file with mode: 0644]

diff --git a/arch/ppc/boot/simple/misc-mv64x60.S b/arch/ppc/boot/simple/misc-mv64x60.S
new file mode 100644 (file)
index 0000000..9c809c8
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * arch/ppc/boot/simple/misc-mv64x60.S
+ * 
+ * Code to change the base address of the host bridges and call board specific
+ * init routine.
+ *
+ * Author: Mark Greer <mgreer@mvista.com>
+ *
+ * 2002 (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/config.h>
+#include <asm/ppc_asm.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/mv64x60_defs.h>
+
+       .globl  mv64x60_init
+mv64x60_init:
+       mflr    r27
+
+#if (CONFIG_MV64X60_NEW_BASE != CONFIG_MV64X60_BASE)
+       bl      move_base
+#endif
+       bl      mv64x60_board_init
+
+       mtlr    r27
+       blr
+
+#if (CONFIG_MV64X60_NEW_BASE != CONFIG_MV64X60_BASE)
+move_base:
+       li      r20,0
+       li      r23,20
+
+       /* Relocate bridge's regs */
+        addis  r25,0,CONFIG_MV64X60_BASE@h
+        ori    r25,r25,MV64x60_INTERNAL_SPACE_DECODE             
+        lwbrx  r26,0,(r25)
+       lis     r24,0xffff
+       and     r26,r26,r24
+       addis   r24,0,CONFIG_MV64X60_NEW_BASE@h
+       srw     r24,r24,r23
+       or      r26,r26,r24
+        stwbrx  r26,0,(r25)
+       sync
+
+       /* Wait for write to take effect */
+        addis  r25,0,CONFIG_MV64X60_NEW_BASE@h
+       ori     r25,r25,MV64x60_INTERNAL_SPACE_DECODE
+1:     lwbrx   r24,0,(r25)
+       cmpw    r24,r26
+       bne     1b
+
+       blr
+#endif
diff --git a/arch/ppc/boot/simple/mv64x60_stub.c b/arch/ppc/boot/simple/mv64x60_stub.c
new file mode 100644 (file)
index 0000000..46d9924
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * arch/ppc/boot/simple/mv64x60_stub.c
+ *
+ * Stub for board_init() routine called from mv64x60_init().
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2002 (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.
+ */
+
+long   mv64x60_console_baud = 9600;            /* Default baud: 9600 */
+long   mv64x60_mpsc_clk_src = 8;               /* Default clk src: TCLK */
+long   mv64x60_mpsc_clk_freq = 100000000;      /* Default clk freq: 100 MHz */
+
+void
+mv64x60_board_init(void)
+{
+}
diff --git a/arch/ppc/boot/simple/mv64x60_tty.c b/arch/ppc/boot/simple/mv64x60_tty.c
new file mode 100644 (file)
index 0000000..b1cb21a
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * arch/ppc/boot/simple/mv64x60_tty.c
+ * 
+ * Bootloader version of the embedded MPSC/UART driver for the Marvell 64x60.
+ * Note: Due to a GT64260A erratum, DMA will be used for UART input (via SDMA).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+/* This code assumes that the data cache has been disabled (L1, L2, L3). */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/serial_reg.h>
+#include <asm/serial.h>
+#include <asm/mv64x60_defs.h>
+#include "../../../../drivers/serial/mpsc/mpsc_defs.h"
+
+extern void udelay(long);
+static void stop_dma(int chan);
+
+static u32     mv64x60_base = CONFIG_MV64X60_NEW_BASE;
+
+inline unsigned
+mv64x60_in_le32(volatile unsigned *addr)
+{
+       unsigned ret;
+
+       __asm__ __volatile__("lwbrx %0,0,%1; eieio" : "=r" (ret) :
+                                    "r" (addr), "m" (*addr));
+       return ret;
+}
+
+inline void
+mv64x60_out_le32(volatile unsigned *addr, int val)
+{
+       __asm__ __volatile__("stwbrx %1,0,%2; eieio" : "=m" (*addr) :
+                                    "r" (val), "r" (addr));
+}
+
+#define MV64x60_REG_READ(offs)                                         \
+       (mv64x60_in_le32((volatile uint *)(mv64x60_base + (offs))))
+#define MV64x60_REG_WRITE(offs, d)                                     \
+        (mv64x60_out_le32((volatile uint *)(mv64x60_base + (offs)), (int)(d)))
+
+
+typedef struct {
+       u32     sdc;
+       u32     sdcm;
+       u32     rx_desc;
+       u32     rx_buf_ptr;
+       u32     scrdp;
+       u32     tx_desc;
+       u32     sctdp;
+       u32     sftdp;
+} sdma_regs_t;
+
+static sdma_regs_t  sdma_regs[2];
+
+#define        SDMA_REGS_INIT(s, reg_base) {                   \
+       (s)->sdc        = (reg_base) + SDMA_SDC;        \
+       (s)->sdcm       = (reg_base) + SDMA_SDCM;       \
+       (s)->rx_desc    = (reg_base) + SDMA_RX_DESC;    \
+       (s)->rx_buf_ptr = (reg_base) + SDMA_RX_BUF_PTR; \
+       (s)->scrdp      = (reg_base) + SDMA_SCRDP;      \
+       (s)->tx_desc    = (reg_base) + SDMA_TX_DESC;    \
+       (s)->sctdp      = (reg_base) + SDMA_SCTDP;      \
+       (s)->sftdp      = (reg_base) + SDMA_SFTDP;      \
+}
+
+typedef struct {
+       volatile u16 bufsize;
+       volatile u16 bytecnt;
+       volatile u32 cmd_stat;
+       volatile u32 next_desc_ptr;
+       volatile u32 buffer;
+} mv64x60_rx_desc_t;
+
+typedef struct {
+       volatile u16 bytecnt;
+       volatile u16 shadow;
+       volatile u32 cmd_stat;
+       volatile u32 next_desc_ptr;
+       volatile u32 buffer;
+} mv64x60_tx_desc_t;
+
+#define        MAX_RESET_WAIT  10000
+#define        MAX_TX_WAIT     10000
+
+#define        RX_NUM_DESC     2
+#define        TX_NUM_DESC     2
+
+#define        RX_BUF_SIZE     16
+#define        TX_BUF_SIZE     16
+
+static mv64x60_rx_desc_t rd[2][RX_NUM_DESC] __attribute__ ((aligned(32)));
+static mv64x60_tx_desc_t td[2][TX_NUM_DESC] __attribute__ ((aligned(32)));
+
+static char rx_buf[2][RX_NUM_DESC * RX_BUF_SIZE] __attribute__ ((aligned(32)));
+static char tx_buf[2][TX_NUM_DESC * TX_BUF_SIZE] __attribute__ ((aligned(32)));
+
+static int cur_rd[2] = { 0, 0 };
+static int cur_td[2] = { 0, 0 };
+
+static char chan_initialized[2] = { 0, 0 };
+
+
+#define        RX_INIT_RDP(rdp) {                      \
+       (rdp)->bufsize = 2;                     \
+       (rdp)->bytecnt = 0;                     \
+       (rdp)->cmd_stat = SDMA_DESC_CMDSTAT_L | \
+                         SDMA_DESC_CMDSTAT_F | \
+                         SDMA_DESC_CMDSTAT_O;  \
+}
+
+unsigned long
+serial_init(int chan, void *ignored)
+{
+       u32             mpsc_base, mpsc_routing_base, sdma_base, brg_bcr, cdv;
+       int             i;
+       extern long     mv64x60_console_baud;
+       extern long     mv64x60_mpsc_clk_src;
+       extern long     mv64x60_mpsc_clk_freq;
+
+       chan = (chan == 1);  /* default to chan 0 if anything but 1 */
+
+       if (chan_initialized[chan]) return chan;
+
+       chan_initialized[chan] = 1;
+
+       if (chan == 0) {
+               mpsc_base = MV64x60_MPSC_0_OFFSET;
+               sdma_base = MV64x60_SDMA_0_OFFSET;
+               brg_bcr = MV64x60_BRG_0_OFFSET + BRG_BCR;
+               SDMA_REGS_INIT(&sdma_regs[0], MV64x60_SDMA_0_OFFSET);
+       }
+       else {
+               mpsc_base = MV64x60_MPSC_1_OFFSET;
+               sdma_base = MV64x60_SDMA_1_OFFSET;
+               brg_bcr = MV64x60_BRG_1_OFFSET + BRG_BCR;
+               SDMA_REGS_INIT(&sdma_regs[0], MV64x60_SDMA_1_OFFSET);
+       }
+
+       mpsc_routing_base = MV64x60_MPSC_ROUTING_OFFSET;
+
+       stop_dma(chan);
+
+       /* Set up ring buffers */
+       for (i=0; i<RX_NUM_DESC; i++) {
+               RX_INIT_RDP(&rd[chan][i]);
+               rd[chan][i].buffer = (u32)&rx_buf[chan][i * RX_BUF_SIZE];
+               rd[chan][i].next_desc_ptr = (u32)&rd[chan][i+1];
+       }
+       rd[chan][RX_NUM_DESC - 1].next_desc_ptr = (u32)&rd[chan][0];
+
+       for (i=0; i<TX_NUM_DESC; i++) {
+               td[chan][i].bytecnt = 0;
+               td[chan][i].shadow = 0;
+               td[chan][i].buffer = (u32)&tx_buf[chan][i * TX_BUF_SIZE];
+               td[chan][i].cmd_stat = SDMA_DESC_CMDSTAT_F|SDMA_DESC_CMDSTAT_L;
+               td[chan][i].next_desc_ptr = (u32)&td[chan][i+1];
+       }
+       td[chan][TX_NUM_DESC - 1].next_desc_ptr = (u32)&td[chan][0];
+
+       /* Set MPSC Routing */
+        MV64x60_REG_WRITE(mpsc_routing_base + MPSC_MRR, 0x3ffffe38);
+
+/* XXXX Not for 64360 XXXX*/
+        MV64x60_REG_WRITE(GT64260_MPP_SERIAL_PORTS_MULTIPLEX, 0x00001102);
+
+       /* MPSC 0/1 Rx & Tx get clocks BRG0/1 */
+        MV64x60_REG_WRITE(mpsc_routing_base + MPSC_RCRR, 0x00000100);
+        MV64x60_REG_WRITE(mpsc_routing_base + MPSC_TCRR, 0x00000100);
+
+       /* clear pending interrupts */
+        MV64x60_REG_WRITE(MV64x60_SDMA_INTR_OFFSET + SDMA_INTR_MASK, 0);
+
+       MV64x60_REG_WRITE(SDMA_SCRDP + sdma_base, &rd[chan][0]);
+       MV64x60_REG_WRITE(SDMA_SCTDP + sdma_base, &td[chan][TX_NUM_DESC - 1]);
+       MV64x60_REG_WRITE(SDMA_SFTDP + sdma_base, &td[chan][TX_NUM_DESC - 1]);
+
+       MV64x60_REG_WRITE(SDMA_SDC + sdma_base,
+                 SDMA_SDC_RFT | SDMA_SDC_SFM | SDMA_SDC_BLMR | SDMA_SDC_BLMT |
+                 (3 << 12));
+
+       cdv = ((mv64x60_mpsc_clk_freq/(32*mv64x60_console_baud))-1);
+       MV64x60_REG_WRITE(brg_bcr,
+               ((mv64x60_mpsc_clk_src << 18) | (1 << 16) | cdv));
+
+       /* Put MPSC into UART mode, no null modem, 16x clock mode */
+       MV64x60_REG_WRITE(MPSC_MMCRL + mpsc_base, 0x000004c4);
+       MV64x60_REG_WRITE(MPSC_MMCRH + mpsc_base, 0x04400400);
+
+        MV64x60_REG_WRITE(MPSC_CHR_1 + mpsc_base, 0);
+        MV64x60_REG_WRITE(MPSC_CHR_9 + mpsc_base, 0);
+        MV64x60_REG_WRITE(MPSC_CHR_10 + mpsc_base, 0);
+        MV64x60_REG_WRITE(MPSC_CHR_3 + mpsc_base, 4);
+        MV64x60_REG_WRITE(MPSC_CHR_4 + mpsc_base, 0);
+        MV64x60_REG_WRITE(MPSC_CHR_5 + mpsc_base, 0);
+        MV64x60_REG_WRITE(MPSC_CHR_6 + mpsc_base, 0);
+        MV64x60_REG_WRITE(MPSC_CHR_7 + mpsc_base, 0);
+        MV64x60_REG_WRITE(MPSC_CHR_8 + mpsc_base, 0);
+
+       /* 8 data bits, 1 stop bit */
+       MV64x60_REG_WRITE(MPSC_MPCR + mpsc_base, (3 << 12));
+       MV64x60_REG_WRITE(SDMA_SDCM + sdma_base, SDMA_SDCM_ERD);
+       MV64x60_REG_WRITE(MPSC_CHR_2 + mpsc_base, MPSC_CHR_2_EH);
+
+       udelay(100);
+
+       return chan;
+}
+
+static void
+stop_dma(int chan)
+{
+       int     i;
+
+       /* Abort SDMA Rx, Tx */
+       MV64x60_REG_WRITE(sdma_regs[chan].sdcm, SDMA_SDCM_AR | SDMA_SDCM_STD);
+
+       for (i=0; i<MAX_RESET_WAIT; i++) {
+               if ((MV64x60_REG_READ(sdma_regs[chan].sdcm) &
+                       (SDMA_SDCM_AR | SDMA_SDCM_AT)) == 0) {
+                       break;
+               }
+               udelay(100);
+       }
+
+       return;
+}
+
+static int
+wait_for_ownership(int chan)
+{
+       int     i;
+
+       for (i=0; i<MAX_TX_WAIT; i++) {
+               if ((MV64x60_REG_READ(sdma_regs[chan].sdcm) &
+                                       SDMA_SDCM_TXD) == 0)
+                       break;
+               udelay(1000);
+       }
+
+       return (i < MAX_TX_WAIT);
+}
+
+void
+serial_putc(unsigned long com_port, unsigned char c)
+{
+       mv64x60_tx_desc_t       *tdp;
+
+       if (wait_for_ownership(com_port) == 0) return;
+
+       tdp = &td[com_port][cur_td[com_port]];
+       if (++cur_td[com_port] >= TX_NUM_DESC) cur_td[com_port] = 0;
+
+       *(unchar *)(tdp->buffer ^ 7) = c;
+       tdp->bytecnt = 1;
+       tdp->shadow = 1;
+       tdp->cmd_stat = SDMA_DESC_CMDSTAT_L | SDMA_DESC_CMDSTAT_F |
+               SDMA_DESC_CMDSTAT_O;
+
+       MV64x60_REG_WRITE(sdma_regs[com_port].sctdp, tdp);
+       MV64x60_REG_WRITE(sdma_regs[com_port].sftdp, tdp);
+       MV64x60_REG_WRITE(sdma_regs[com_port].sdcm,
+               MV64x60_REG_READ(sdma_regs[com_port].sdcm) | SDMA_SDCM_TXD);
+
+       return;
+}
+
+unsigned char
+serial_getc(unsigned long com_port)
+{
+       mv64x60_rx_desc_t       *rdp;
+       unchar                  c = '\0';
+
+       rdp = &rd[com_port][cur_rd[com_port]];
+
+       if ((rdp->cmd_stat & (SDMA_DESC_CMDSTAT_O|SDMA_DESC_CMDSTAT_ES)) == 0) {
+               c = *(unchar *)(rdp->buffer ^ 7);
+               RX_INIT_RDP(rdp);
+               if (++cur_rd[com_port] >= RX_NUM_DESC) cur_rd[com_port] = 0;
+       }
+
+       return c;
+}
+
+int
+serial_tstc(unsigned long com_port)
+{
+       mv64x60_rx_desc_t       *rdp;
+       int                     loop_count = 0;
+       int                     rc = 0;
+
+       rdp = &rd[com_port][cur_rd[com_port]];
+
+       /* Go thru rcv desc's until empty looking for one with data (no error)*/
+       while (((rdp->cmd_stat & SDMA_DESC_CMDSTAT_O) == 0) &&
+              (loop_count++ < RX_NUM_DESC)) {
+
+               /* If there was an error, reinit the desc & continue */
+               if ((rdp->cmd_stat & SDMA_DESC_CMDSTAT_ES) != 0) {
+                       RX_INIT_RDP(rdp);
+                       if (++cur_rd[com_port] >= RX_NUM_DESC) {
+                               cur_rd[com_port] = 0;
+                       }
+                       rdp = (mv64x60_rx_desc_t *)rdp->next_desc_ptr;
+               }
+               else {
+                       rc = 1;
+                       break;
+               }
+       }
+
+       return rc;
+}
+
+void
+serial_close(unsigned long com_port)
+{
+       stop_dma(com_port);
+       return;
+}
diff --git a/arch/ppc/platforms/dmv182.c b/arch/ppc/platforms/dmv182.c
new file mode 100644 (file)
index 0000000..dd57e16
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * arch/ppc/platforms/dmv182p.c
+ * Setup code for the Dy-4 SVME/DMV-182
+ *
+ * Copyright (C) 2004 TimeSys Corporation
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Original 2.4 port by Scott Wood <scott.wood@timesys.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/initrd.h>
+#include <linux/root_dev.h>
+#include <linux/delay.h>
+
+#include <asm/serial.h>
+#include <asm/bootinfo.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/mv64x60.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/todc.h>
+#include <linux/tty.h>  /* for linux/serial_core.h */
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+#include "dmv182.h"
+
+extern int mv64360_get_irq(struct pt_regs *regs);
+extern void mv64360_init_irq(void);
+
+extern void gen550_progress(char *s, unsigned short hex);
+extern void gen550_init(int, struct uart_port *);
+
+static void __init dmv182_setup_peripherals(void);
+static void __init dmv182_setup_bridge(void);
+
+static struct mv64x60_handle bh;
+
+TODC_ALLOC();
+
+static void __init dmv182_map_io(void)
+{
+       io_block_mapping((unsigned long)dmv182_board_io_virt,
+                        dmv182_board_io_phys, 0x10000000, _PAGE_IO);
+}
+
+// This sets up BAT3 to cover the serial port and Discovery chip.
+
+static void __init dmv182_setup_bats(void)
+{
+       int tmp1, tmp2;
+
+       asm volatile("lis %0, 0xe000;"
+                    "ori %0, %0, 0x002a;"
+                    "lis %1, 0xf000;"
+                    "ori %1, %1, 0x1ffe;"
+                    "mtspr %2, %0;"
+                    "mtspr %3, %1"
+                  : "=r" (tmp1), "=r" (tmp2)
+                  : "i" (DBAT3L), "i" (DBAT3U)
+                  : "memory");
+}
+
+static u8 *const irqstat = dmv182_fpga_io + 0x80;
+static u8 *const irqmask = dmv182_fpga_io + 0x81;
+
+// These two functions transform an IRQ number into
+// byte and bit indices into the above arrays.
+
+static inline int irqreg(unsigned int irq)
+{
+       return ((irq - 96) >> 3) * 3;
+}
+
+static inline int irqbit(unsigned int irq)
+{
+       return (irq - 96) & 7;
+}
+
+// FIXME: CPU1 and affinity support
+// The Marvell code doesn't appear to support anything
+// other than doorbells on CPU1 at the moment.
+
+static void dmv182_mask_irq(unsigned int irq)
+{
+       irqmask[irqreg(irq)] &= ~(1 << irqbit(irq));
+}
+
+static void dmv182_unmask_irq(unsigned int irq)
+{
+       irqmask[irqreg(irq)] |= 1 << irqbit(irq);
+}
+
+static unsigned int dmv182_startup_irq(unsigned int irq)
+{
+       dmv182_unmask_irq(irq);
+       return 0;
+}
+
+struct hw_interrupt_type dmv182_pic = {
+       .typename     = " DMV182_PIC ",
+       .startup      = dmv182_startup_irq,
+       .shutdown     = dmv182_mask_irq,
+       .enable       = dmv182_unmask_irq,
+       .disable      = dmv182_mask_irq,
+       .ack          = dmv182_mask_irq,
+       .end          = dmv182_unmask_irq,
+       .set_affinity = NULL
+};
+
+atomic_t spurious_interrupts;
+
+static irqreturn_t dmv182_cascade(int irq, void *dev_id, struct pt_regs *regs)
+{
+       int i, j;
+       int cpu = smp_processor_id();
+       int irqs;
+
+       for (i = 0, j = 96; i < 24; i += 3, j += 8) {
+               irqs = irqstat[i] & irqmask[i + cpu];
+               
+               if (irqs)
+                       break;
+       }
+       
+       if (i < 24) {
+               ppc_irq_dispatch_handler(regs, j + ffs(irqs) - 1);
+               return IRQ_HANDLED;
+       }
+
+       atomic_inc(&spurious_interrupts);
+       return IRQ_NONE;
+}
+
+#ifdef CONFIG_SMP
+static irqreturn_t dmv182_doorbell(int irq, void *dev_id, struct pt_regs *regs);
+#endif
+
+static void __init dmv182_init_irq(void)
+{
+       int i;
+
+       if (ppc_md.progress)
+               ppc_md.progress("dmv182_init_irq", 0x1821);
+       
+       for (i = 96; i < 160; i++) {
+               dmv182_mask_irq(i);
+               irqmask[irqreg(i) + 1] &= ~(1 << irqbit(i));
+               irq_desc[i].handler = &dmv182_pic;
+               irq_desc[i].status = IRQ_LEVEL | IRQ_DISABLED;
+       }
+       
+       mv64360_init_irq();
+       
+       if (request_irq(94, dmv182_cascade, SA_INTERRUPT,
+                       "DMV182 CPU0 cascade", NULL) < 0)
+       {
+               panic("Could not request CPU0 cascade IRQ\n");
+       }
+
+#ifdef CONFIG_SMP
+#if 0
+       if (request_irq(95, dmv182_cascade, SA_INTERRUPT,
+                       "DMV182 CPU1 cascade", NULL) < 0)
+       {
+               panic("Could not request CPU1 cascade IRQ\n");
+       }
+#endif
+
+       if (request_irq(60, dmv182_doorbell, SA_INTERRUPT,
+                       "CPU0 doorbell", NULL) < 0)
+       {
+               panic("Could not request CPU1 doorbell IRQ\n");
+       }
+
+       if (request_irq(28, dmv182_doorbell, SA_INTERRUPT,
+                       "CPU1 doorbell", NULL) < 0)
+       {
+               panic("Could not request CPU1 doorbell IRQ\n");
+       }
+       
+       // Clear and unmask all doorbell interrupts.
+       
+       mv64x60_write(&bh, MV64360_CPU0_DOORBELL_CLR, 0xff);
+       mv64x60_write(&bh, MV64360_CPU1_DOORBELL_CLR, 0xff);
+       mv64x60_write(&bh, MV64360_CPU0_DOORBELL_MASK, 0xff);
+       mv64x60_write(&bh, MV64360_CPU1_DOORBELL_MASK, 0xff);
+#endif
+}
+
+// It's really device numbers, not idsels, but we have
+// to call it that so the PCI_IRQ_TABLE_LOOKUP will work.
+
+static int __init dmv182_map_irq(struct pci_dev *dev,
+                                  unsigned char idsel,
+                                  unsigned char pin)
+{
+       struct pci_controller *hose = pci_bus_to_hose(dev->bus->number);
+
+#if 0
+       printk("map irq: hose %d, bus %d, slot %d, first %d\n", hose->index,
+              dev->bus->number, idsel, hose->first_busno);
+#endif
+
+       if (hose->index != 0 && hose->index != 1) {
+               printk(KERN_ERR "map_irq: unknown hose %d\n", hose->index);
+               return 0;
+       }
+       
+       // Some of this is guesswork...
+       // In particular, I don't know if the ABCD mappings are right,
+       // and I don't know which IPM goes with which slot (the manual
+       // merely says "IPM" for both).
+
+       if (hose->index == 0) {
+               static u8 pci_irq_table[][4] = 
+               /*
+                * PCI IDSEL/INTPIN->INTLINE
+                *  A   B   C   D
+                */
+               {
+                       { DMV182_IRQ_PMC1A, DMV182_IRQ_PMC1B,
+                         DMV182_IRQ_PMC1C, DMV182_IRQ_PMC1D }, // PMC Slot 1 A
+                       { DMV182_IRQ_PMC1A, DMV182_IRQ_PMC1B,
+                         DMV182_IRQ_PMC1C, DMV182_IRQ_PMC1D }, // PMC Slot 1 B
+               };
+               
+               const int min_idsel = 4, max_idsel = 5, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       } else if (dev->bus->parent && dev->bus->primary == hose->first_busno &&
+                  dev->bus->self->devfn == 0x10) {
+               static u8 pci_irq_table[][4] = 
+               /*
+                * PCI IDSEL/INTPIN->INTLINE
+                *  A   B   C   D
+                */
+               {
+                       { DMV182_IRQ_IPM0, DMV182_IRQ_IPM0,
+                         DMV182_IRQ_IPM0, DMV182_IRQ_IPM0 }, // IPM... 0?
+                       { DMV182_IRQ_IPM1, DMV182_IRQ_IPM1,
+                         DMV182_IRQ_IPM1, DMV182_IRQ_IPM1 }, // IPM... 1?
+                       { DMV182_IRQ_USB_A, DMV182_IRQ_USB_B,
+                         DMV182_IRQ_USB_C, DMV182_IRQ_USB_SMI }, // USB
+                       { DMV182_IRQ_VME_CPU0, DMV182_IRQ_VME_CPU1, 0, 0 }, // VME
+               };
+               
+               const int min_idsel = 1, max_idsel = 4, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       } else {
+               static u8 pci_irq_table[][4] = 
+               /*
+                * PCI IDSEL/INTPIN->INTLINE
+                *  A   B   C   D
+                */
+               {
+                       { DMV182_IRQ_PMC2A, DMV182_IRQ_PMC2B,
+                         DMV182_IRQ_PMC2C, DMV182_IRQ_PMC2D }, // PMC Slot 2 A
+                       { DMV182_IRQ_PMC2A, DMV182_IRQ_PMC2B,
+                         DMV182_IRQ_PMC2C, DMV182_IRQ_PMC2D }, // PMC Slot 2 B
+               };
+               
+               const int min_idsel = 4, max_idsel = 5, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       }
+}
+
+static unsigned char dmv182_pci_swizzle(struct pci_dev *dev,
+                                         unsigned char *pinp)
+{
+       struct pci_controller *hose = dev->sysdata;
+
+       // The devices under this particular bridge have their IRQs
+       // directly routed to the PIC, rather than through the parent
+       // bus.  Thus, don't swizzle them.  The bus is determined by
+       // the devfn of the parent, rather than its own bus number,
+       // in case a PMC card is added that has its own bridge(s),
+       // causing the numbering to change.
+       
+       if (hose->index == 1 && dev->bus->parent &&
+           dev->bus->primary == hose->first_busno &&
+           dev->bus->self->devfn == 0x10)
+               return PCI_SLOT(dev->devfn);
+       
+       return common_swizzle(dev, pinp);
+}
+
+static unsigned long __init
+dmv182_pci_bridge_reserve_space(struct pci_controller *hose,
+                                 unsigned char bus, unsigned char devfn)
+{
+       // Reserve 768 MiB for the bus containing VME.  This
+       // will allow one to map the entire RAM of a 512 MiB
+       // card over VME, while still allowing space for other
+       // stuff on the bridge.
+       if (hose->first_busno == bus && devfn == 0x10)
+               return 0x30000000;
+       
+       return 0;
+}
+
+static void __init dmv182_setup_caches(void)
+{
+#if 0 // This actually causes the TimeSys 2.4 port to blow up too, for me 
+
+       // Why can't L2CR be set by generic 745x code?
+       // And what's with the underscore?
+       _set_L2CR(0xc0000000);
+
+       _set_L3CR(0x9e8a0180);
+#endif
+}
+
+#ifdef CONFIG_SERIAL_8250
+static void __init dmv182_early_serial_map(void)
+{
+        struct uart_port uart_req;
+       void *membase = ioremap(0xe0010000, PAGE_SIZE);
+
+        /* Setup serial port access */
+        memset(&uart_req, 0, sizeof (uart_req));
+        uart_req.irq = DMV182_IRQ_SERIAL_CH1;
+        uart_req.flags = 0;
+       uart_req.type = PORT_16550;
+        uart_req.uartclk = BASE_BAUD * 16;
+        uart_req.iotype = SERIAL_IO_MEM;
+        uart_req.mapbase = (unsigned long)dmv182_fpga_io + 0x18;
+        uart_req.membase = membase + 0x18;
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+        gen550_init(0, &uart_req);
+#endif
+
+        if (early_serial_setup(&uart_req) != 0)
+                printk("Early serial init of port 0 failed\n");
+
+        /* Assume early_serial_setup() doesn't modify uart_req */
+        uart_req.line = 1;
+        uart_req.mapbase = (unsigned long)dmv182_fpga_io + 0x20;
+        uart_req.membase = membase + 0x20;
+        uart_req.irq = DMV182_IRQ_SERIAL_CH2;
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+        gen550_init(1, &uart_req);
+#endif
+
+        if (early_serial_setup(&uart_req) != 0)
+                printk("Early serial init of port 1 failed\n");
+}
+#endif
+
+static void __init dmv182_setup_arch(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("dmv182_setup_arch", 0x1820);
+
+       ppc_md.pci_swizzle = dmv182_pci_swizzle;
+
+       dmv182_setup_caches();
+
+       // Enable snooping.
+//     MV_SET_REG_BITS(MV64360_CPU_MASTER_CONTROL, (1 << 12) | (1 << 13));
+
+       // Set up the RTC.
+       dmv182_setup_bridge();
+       dmv182_setup_peripherals();
+
+#ifdef CONFIG_SERIAL_8250
+       dmv182_early_serial_map();
+#endif
+       if (ppc_md.progress)
+               ppc_md.progress("dmv182_setup_arch end", 0x182f);
+}
+
+static void __init dmv182_calibrate_decr(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("dmv182_calibrate_decr", 0x1822);
+       
+       tb_ticks_per_jiffy = 25000000 / HZ;
+       tb_to_us = mulhwu_scale_factor(25000000, 1000000);
+}
+
+static void dmv182_halt(void)
+{
+       local_irq_disable();
+       for(;;);
+}
+
+static void dmv182_restart(char *cmd)
+{
+       unsigned long reg;
+       volatile unsigned long *ptr = NULL;
+       struct pci_dev *dev;
+
+       local_irq_disable();
+
+       /* 
+        * The best way to reset the board is through the Universe VME. 
+        * Since the VME driver may or may not be loaded, we can't rely
+        * on that, so the best way I can think of in resetting the board
+        * is to search all the PCI devices looking for the Universe chip 
+        * and write to its command register to reset the board.
+        */
+       dev = pci_find_device(PCI_VENDOR_ID_TUNDRA, 0, NULL);
+       if (dev) {
+               printk("Found VME device %s\n",dev->slot_name);
+
+               for (reg = 0; reg < 6; reg++) {
+                       struct resource *res = dev->resource + reg;
+                       if ((res->flags & PCI_BASE_ADDRESS_SPACE) ==
+                           PCI_BASE_ADDRESS_SPACE_MEMORY) {
+                               ptr = ioremap(res->start + 0x404, sizeof(ptr)); /* CTRL_REG */
+                               break;
+                       }
+               }
+       }
+
+       if (!ptr) {
+               printk("No VME device found to reset board\n");
+               return;
+       }
+
+       printk("**** resetting board through VME ****\n");
+       mdelay(10);
+
+       reg = *ptr;
+       reg |= 0x8000; /* reset only the board and not the entire chassis. */
+       *ptr = reg;
+
+       for(;;);
+}
+
+void board_get_mac(int port, u8 *addr)
+{
+       if (port < 1 || port > 2) {
+               printk(KERN_ERR "Unknown port %d in board_get_mac()...\n", port);
+               return;
+       }
+       
+       memcpy(addr, (u8 *)dmv182_nvram + 8 + (2 - port) * 6, 6);
+       printk(KERN_NOTICE "Ethernet port %d MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+              port, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+#ifdef CONFIG_SMP
+
+static int dmv182_smp_probe(void)
+{
+       return 2;
+}
+
+void __secondary_start(void);
+
+static void dmv182_kick_cpu(int cpu)
+{
+       BUG_ON(cpu != 1);
+
+       *(u32 *)(PAGE_OFFSET + 4) = (u32)__secondary_start - PAGE_OFFSET;
+       wmb();
+       *(u32 *)(PAGE_OFFSET + 0) = 0x38a3fd19;
+       wmb();
+
+       /* Set MaskBR1 to allow CPU1 to get access to the bus. */
+       mv64x60_modify(&bh, MV64x60_CPU_MASTER_CNTL, 0, 1<<9);
+}
+
+static void dmv182_setup_cpu(int cpu)
+{
+       int whoami = mv64x60_read(&bh, MV64360_WHO_AM_I);
+       
+       if (cpu != whoami) {
+               printk("CPU %d whoami %d\n", cpu, whoami);
+               BUG();
+       }
+
+       // Enable broadcasting of synchronization and cache/tlb
+       // flushing/invalidation instructions
+       
+       mtspr(SPRN_HID1, mfspr(SPRN_HID1) | HID1_ABE | HID1_SYNCBE);
+       asm volatile("sync; isync" : : : "memory");
+
+       if (cpu == 1)
+               dmv182_setup_caches();
+}
+
+static void dmv182_message_pass(int target, int msg, ulong data, int wait)
+{
+       int i;
+       int reg;
+       
+       if (unlikely(msg < 0 || msg > 7)) {
+               printk(KERN_ERR "dmv182_message_pass: bad message %x\n", msg);
+               return;
+       }
+       
+       for_each_online_cpu(i) {
+               reg = MV64360_CPUx_DOORBELL(i);
+
+               if (target == MSG_ALL ||
+                   (target == MSG_ALL_BUT_SELF && i != smp_processor_id()) ||
+                   target == i)
+                       mv64x60_modify(&bh, reg, 1 << msg, 1 << msg);
+       }
+}
+
+static irqreturn_t dmv182_doorbell(int irq, void *dev_id, struct pt_regs *regs)
+{
+       u32 bits = mv64x60_read(&bh, MV64360_CPUx_DOORBELL(smp_processor_id()));
+
+       bits &= 0xff;
+
+       mv64x60_write(&bh, MV64360_CPU0_DOORBELL_CLR +
+                     smp_processor_id() * 0x10, bits);
+
+       while (bits) {
+               int msg = __ilog2(bits);;
+               smp_message_recv(msg, regs);
+               bits &= ~(1 << msg);
+       }
+       return IRQ_HANDLED;
+}
+
+static struct smp_ops_t dmv182_smp_ops = {
+       .probe        = dmv182_smp_probe,
+       .kick_cpu     = dmv182_kick_cpu,
+       .setup_cpu    = dmv182_setup_cpu,
+       .message_pass = dmv182_message_pass,
+       .give_timebase = smp_generic_give_timebase,
+       .take_timebase = smp_generic_take_timebase,
+};
+
+#endif
+
+static void __init dmv182_setup_bridge(void)
+{
+       mv64x60_setup_info_t si;
+
+       memset(&si, 0, sizeof(si));
+
+       si.phys_reg_base = CONFIG_MV64X60_NEW_BASE;
+       si.map_irq = dmv182_map_irq;
+
+       si.pci_0.enable_bus = 1;
+       si.pci_0.enumerate_bus = 1;
+       si.pci_0.pci_io.cpu_base = 0xa0000000;
+       si.pci_0.pci_io.pci_base_hi = 0;
+       si.pci_0.pci_io.pci_base_lo = 0;
+       si.pci_0.pci_io.size = 0x01000000;
+       si.pci_0.pci_io.swap = 0x01000000; /* XXXX No swapping */
+       si.pci_0.pci_mem[0].cpu_base = 0x80000000;
+       si.pci_0.pci_mem[0].pci_base_hi = 0;
+       si.pci_0.pci_mem[0].pci_base_lo = 0x80000000;
+       si.pci_0.pci_mem[0].size = 0x10000000;
+       si.pci_0.pci_mem[0].swap = 0x01000000; /* XXXX No swapping */
+       si.pci_0.pci_mem[1].cpu_base = 0;
+       si.pci_0.pci_mem[1].pci_base_hi = 0;
+       si.pci_0.pci_mem[1].pci_base_lo = 0;
+       si.pci_0.pci_mem[1].size = 0;   /* Don't use this window */
+       si.pci_0.pci_mem[1].swap = 0;
+       si.pci_0.pci_mem[2].cpu_base = 0;
+       si.pci_0.pci_mem[2].pci_base_hi = 0;
+       si.pci_0.pci_mem[2].pci_base_lo = 0;
+       si.pci_0.pci_mem[2].size = 0;   /* Don't use this window */
+       si.pci_0.pci_mem[1].swap = 0;
+       si.pci_0.pci_cmd_bits = 0;
+       si.pci_0.latency_timer = 0x8;
+
+       si.pci_1.enable_bus = 1;
+       si.pci_1.enumerate_bus = 1;
+       si.pci_1.pci_io.cpu_base = 0xa1000000;
+       si.pci_1.pci_io.pci_base_hi = 0;
+       si.pci_1.pci_io.pci_base_lo = 0x01000000;
+       si.pci_1.pci_io.size = 0x01000000;
+       si.pci_1.pci_io.swap = 0x01000000; /* XXXX No swapping */
+       si.pci_1.pci_mem[0].cpu_base = 0x90000000;
+       si.pci_1.pci_mem[0].pci_base_hi = 0;
+       si.pci_1.pci_mem[0].pci_base_lo = 0x90000000;
+       si.pci_1.pci_mem[0].size = 0x10000000;
+       si.pci_1.pci_mem[0].swap = 0x01000000; /* XXXX No swapping */
+       si.pci_1.pci_mem[1].cpu_base = 0;
+       si.pci_1.pci_mem[1].pci_base_hi = 0;
+       si.pci_1.pci_mem[1].pci_base_lo = 0;
+       si.pci_1.pci_mem[1].size = 0;   /* Don't use this window */
+       si.pci_1.pci_mem[1].swap = 0;
+       si.pci_1.pci_mem[2].cpu_base = 0;
+       si.pci_1.pci_mem[2].pci_base_hi = 0;
+       si.pci_1.pci_mem[2].pci_base_lo = 0;
+       si.pci_1.pci_mem[2].size = 0;   /* Don't use this window */
+       si.pci_1.pci_mem[1].swap = 0;
+       si.pci_1.pci_cmd_bits = 0;
+       si.pci_1.latency_timer = 0x8;
+       si.pci_1.pci_cmd_bits = 0;
+       si.pci_1.latency_timer = 0x8;
+
+       si.window_preserve_mask_32 = 0x1f0;
+#if 0
+       for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) {
+               si.cpu_prot_options[i] = 0;
+//             si.cpu_snoop_options[i] = GT64260_CPU_SNOOP_WB;
+               si.pci_0.acc_cntl_options[i] =
+                       /* Breaks PCI (especially slot 4)
+                       GT64260_PCI_ACC_CNTL_PREFETCHEN |
+                       */
+                       GT64260_PCI_ACC_CNTL_DREADEN |
+                       GT64260_PCI_ACC_CNTL_RDPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDMULPREFETCH |
+                       GT64260_PCI_ACC_CNTL_SWAP_NONE |
+                       GT64260_PCI_ACC_CNTL_MBURST_32_BTYES;
+               si.pci_0.snoop_options[i] = GT64260_PCI_SNOOP_WB;
+               si.pci_1.acc_cntl_options[i] =
+                       /* Breaks PCI (especially slot 4)
+                       GT64260_PCI_ACC_CNTL_PREFETCHEN |
+                       */
+                       GT64260_PCI_ACC_CNTL_DREADEN |
+                       GT64260_PCI_ACC_CNTL_RDPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDMULPREFETCH |
+                       GT64260_PCI_ACC_CNTL_SWAP_NONE |
+                       GT64260_PCI_ACC_CNTL_MBURST_32_BTYES;
+//             si.pci_1.snoop_options[i] = GT64260_PCI_SNOOP_WB;
+       }
+#endif
+
+       mv64x60_pci_exclude_bridge = 0;
+
+        /* Lookup PCI host bridges */
+        if (mv64x60_init(&bh, &si)) {
+                printk("Bridge initialization failed.\n");
+        }
+
+       return;
+}
+
+static void __init dmv182_setup_peripherals(void)
+{
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN,
+                                0xf0000000, 0x08000000, 0); // FLASH
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN,
+                                0xe0010000, 0x10000, 0); // I/O FPGA
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN,
+                                0xe0000000, 0x10000, 0); // EPLD
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN,
+                                0xe0020000, 0x10000, 0); // RTC
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN,
+                                0xe0030000, 0x10000, 0); // NVRAM
+
+       TODC_INIT(TODC_TYPE_DS1501, 0, 0, dmv182_rtc, 8);
+}
+
+unsigned long __init dmv182_find_end_of_memory(void)
+{
+#if 0
+       return mv64x60_get_mem_size(0xfff00000 /*CONFIG_MV64X60_NEW_BASE*/,
+                                                 MV64x60_TYPE_MV64360);
+#endif
+       /* But it dies if we enable more than 512MiB. Debug later... */
+       return 0x20000000;
+}
+
+void __init platform_init(unsigned long r3, unsigned long r4,
+                          unsigned long r5, unsigned long r6,
+                          unsigned long r7)
+{
+       parse_bootinfo(find_bootinfo());
+       
+       dmv182_setup_bats();
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG)
+       ppc_md.progress = gen550_progress;
+#endif
+       ppc_md.setup_io_mappings = dmv182_map_io;
+       ppc_md.find_end_of_memory = dmv182_find_end_of_memory;
+       ppc_md.setup_arch = dmv182_setup_arch;
+       ppc_md.init_IRQ = dmv182_init_irq;
+       ppc_md.get_irq = mv64360_get_irq;
+       ppc_md.calibrate_decr = dmv182_calibrate_decr;
+//     ppc_md.pci_bridge_reserve_space = dmv182_pci_bridge_reserve_space;
+       
+       ppc_md.halt = dmv182_halt;
+       ppc_md.power_off = dmv182_halt;
+       ppc_md.restart = dmv182_restart;
+#ifdef CONFIG_SMP
+       ppc_md.smp_ops = &dmv182_smp_ops;
+#endif
+#ifdef CONFIG_GEN_RTC
+       ppc_md.time_init = todc_time_init;
+       ppc_md.set_rtc_time = todc_set_rtc_time;
+       ppc_md.get_rtc_time = todc_get_rtc_time;
+
+       ppc_md.nvram_read_val = todc_direct_read_val;
+       ppc_md.nvram_write_val = todc_direct_write_val;
+#endif
+}
diff --git a/arch/ppc/platforms/dmv182.h b/arch/ppc/platforms/dmv182.h
new file mode 100644 (file)
index 0000000..fde6ba7
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef __DMV182_H
+#define __DMV182_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+
+#define dmv182_board_io_phys 0xe0000000
+#define dmv182_board_io_size 0x00040000
+
+#ifdef __BOOTER__
+#define dmv182_board_io_virt ((u8 *)dmv182_board_io_phys)
+#else
+#define dmv182_board_io_virt ((u8 *)0xf0000000)
+#endif
+
+#define dmv182_fpga_io (dmv182_board_io_virt + 0x10000)
+#define dmv182_rtc     (dmv182_board_io_virt + 0x20000)
+#define dmv182_nvram   (dmv182_board_io_virt + 0x30000)
+
+// This has to go above the mv64360 interrupts, as even though
+// the mv64360 code can handle relocating its interrupt range,
+// the device drivers themselves are oblivious to this.
+
+#define DMV182_IRQ_TEMPA        96
+#define DMV182_IRQ_TEMPB        97
+#define DMV182_IRQ_TEMPC        98
+#define DMV182_IRQ_TEMPD        99
+#define DMV182_IRQ_PMC1A       100
+#define DMV182_IRQ_PMC1B       101
+#define DMV182_IRQ_PMC1C       102
+#define DMV182_IRQ_PMC1D       103
+#define DMV182_IRQ_PMC2A       104
+#define DMV182_IRQ_PMC2B       105
+#define DMV182_IRQ_PMC2C       106
+#define DMV182_IRQ_PMC2D       107
+#define DMV182_IRQ_ENET_PHY2   108
+#define DMV182_IRQ_ENET_PHY1   109
+#define DMV182_IRQ_IPM0        110
+#define DMV182_IRQ_IPM1        111
+#define DMV182_IRQ_USB_A       112
+#define DMV182_IRQ_USB_B       113
+#define DMV182_IRQ_USB_C       114
+#define DMV182_IRQ_USB_SMI     115
+#define DMV182_IRQ_RTC         116
+#define DMV182_IRQ_WDOG_CPU0   117
+#define DMV182_IRQ_WDOG_CPU1   118
+#define DMV182_IRQ_TIMER0_CPU0 120
+#define DMV182_IRQ_TIMER1_CPU0 121
+#define DMV182_IRQ_TIMER2_CPU0 122
+#define DMV182_IRQ_TIMER0_CPU1 123
+#define DMV182_IRQ_TIMER1_CPU1 124
+#define DMV182_IRQ_TIMER2_CPU1 125
+#define DMV182_IRQ_SERIAL_CH1  126
+#define DMV182_IRQ_SERIAL_CH2  127
+#define DMV182_IRQ_VME_CPU0    128
+#define DMV182_IRQ_VME_CPU1    129
+
+// 28 FPGA interrupts starting from here
+#define DMV182_IRQ_FPGA        132
+
+#endif
diff --git a/arch/ppc/platforms/dmv182_serial.h b/arch/ppc/platforms/dmv182_serial.h
new file mode 100644 (file)
index 0000000..b1dbb2d
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __DMV182_SERIAL_H
+#define __DMV182_SERIAL_H
+
+#include <linux/serial.h>
+#include <platforms/dmv182.h>
+
+#define BASE_BAUD (36864000 / 16)
+#define RS_TABLE_SIZE 2
+
+#define STD_UART_OP(num) \
+       { .baud_base = BASE_BAUD, \
+         .irq = DMV182_IRQ_SERIAL_CH##num, \
+         .flags = ASYNC_SKIP_TEST | ASYNC_BUGGY_UART, \
+         .iomem_base = dmv182_fpga_io + 0x18 + 8 * (num - 1), \
+         .io_type = SERIAL_IO_MEM },
+
+#define SERIAL_PORT_DFNS STD_UART_OP(1) STD_UART_OP(2)
+
+#endif
diff --git a/arch/ppc/platforms/ev64260.c b/arch/ppc/platforms/ev64260.c
new file mode 100644 (file)
index 0000000..1275889
--- /dev/null
@@ -0,0 +1,893 @@
+/*
+ * arch/ppc/platforms/ev64260.c
+ *
+ * Board setup routines for the Marvell/Galileo EV-64260-BP Evaluation Board.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2001-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.
+ */
+
+/*
+ * The EV-64260-BP port is the result of hard work from many people from
+ * many companies.  In particular, employees of Marvell/Galileo, Mission
+ * Critical Linux, Xyterra, and MontaVista Software were heavily involved.
+ *
+ * Note: I have not been able to get *all* PCI slots to work reliably
+ *     at 66 MHz.  I recommend setting jumpers J15 & J16 to short pins 1&2
+ *     so that 33 MHz is used. --MAG
+ * Note: The 750CXe and 7450 are not stable with a 125MHz or 133MHz TCLK/SYSCLK.
+ *     At 100MHz, they are solid.
+ */
+#include <linux/config.h>
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+#include <linux/irq.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/console.h>
+#include <linux/initrd.h>
+#include <linux/root_dev.h>
+#if !defined(CONFIG_SERIAL_MPSC_CONSOLE)
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+#endif
+#include <asm/bootinfo.h>
+#include <asm/machdep.h>
+#include <asm/mv64x60.h>
+#include <asm/ppcboot.h>
+#include <asm/todc.h>
+#include <asm/time.h>
+#include <asm/ocp.h>
+
+#include <platforms/ev64260.h>
+
+#define BOARD_VENDOR   "Marvell/Galileo"
+#define BOARD_MACHINE  "EV-64260-BP"
+
+/* Set IDE controllers into Native mode? */
+/* XXXX
+#define SET_PCI_IDE_NATIVE
+*/
+
+ulong  ev64260_mem_size = 0;
+bd_t   ppcboot_bd;
+int    ppcboot_bd_valid=0;
+
+static mv64x60_handle_t        bh;
+
+#if !defined(CONFIG_SERIAL_MPSC_CONSOLE)
+extern void gen550_progress(char *, unsigned short);
+extern void gen550_init(int, struct serial_struct *);
+#endif
+
+static const unsigned int cpu_7xx[16] = { /* 7xx & 74xx (but not 745x) */
+       18, 15, 14, 2, 4, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0
+};
+static const unsigned int cpu_745x[2][16] = { /* PLL_EXT 0 & 1 */
+       { 1, 15, 14,  2,  4, 13,  5,  9,  6, 11,  8, 10, 16, 12,  7,  0 },
+       { 0, 30,  0,  2,  0, 26,  0, 18,  0, 22, 20, 24, 28, 32,  0,  0 }
+};
+
+
+TODC_ALLOC();
+
+static int
+ev64260_get_bus_speed(void)
+{
+       int     speed;
+
+       if (ppcboot_bd_valid) {
+               speed = ppcboot_bd.bi_busfreq;
+       }
+       else {
+               speed = 100000000; /* Only 100MHz is stable */
+       }
+
+       return speed;
+}
+
+static int
+ev64260_get_cpu_speed(void)
+{
+       unsigned long   pvr, hid1, pll_ext;
+
+       pvr = PVR_VER(mfspr(PVR));
+
+       if (pvr != PVR_VER(PVR_7450)) {
+               hid1 = mfspr(HID1) >> 28;
+               return ev64260_get_bus_speed() * cpu_7xx[hid1]/2;
+       }
+       else {
+               hid1 = (mfspr(HID1) & 0x0001e000) >> 13;
+               pll_ext = 0; /* No way to read; must get from schematic */
+               return ev64260_get_bus_speed() * cpu_745x[pll_ext][hid1]/2;
+       }
+}
+
+unsigned long __init
+ev64260_find_end_of_memory(void)
+{
+       if(!ppcboot_bd_valid) {
+               return mv64x60_get_mem_size(CONFIG_MV64X60_NEW_BASE,
+                       MV64x60_TYPE_GT64260A);
+       }
+       return ppcboot_bd.bi_memsize;
+}
+
+#if 0  /* XXXX */
+#ifdef SET_PCI_IDE_NATIVE
+static void __init
+set_pci_native_mode(void)
+{
+       struct pci_dev *dev;
+
+       /* Better way of doing this ??? */
+       pci_for_each_dev(dev) { 
+               int class = dev->class >> 8;
+
+               /* enable pci native mode */
+               if (class == PCI_CLASS_STORAGE_IDE) {
+                       u8 reg;
+
+                       pci_read_config_byte(dev, 0x9, &reg);
+                       if (reg == 0x8a) {
+                               printk("PCI: Enabling PCI IDE native mode on %s\n", dev->slot_name); 
+                               pci_write_config_byte(dev, 0x9,  0x8f);
+
+                               /* let the pci code set this device up after we change it */
+                               pci_setup_device(dev); 
+                       } else if (reg != 0x8f) {
+                               printk("PCI: IDE chip in unknown mode 0x%02x on %s", reg, dev->slot_name);
+                       }
+               }
+       }
+}
+#endif
+#endif
+
+static void __init
+ev64260_pci_fixups(void)
+{
+#ifdef SET_PCI_IDE_NATIVE
+       set_pci_native_mode();
+#endif
+}
+
+
+/*
+ * Marvell/Galileo EV-64260-BP Evaluation Board PCI interrupt routing.
+ * Note: By playing with J8 and JP1-4, you can get 2 IRQ's from the first
+ *     PCI bus (in which cast, INTPIN B would be EV64260_PCI_1_IRQ).
+ *     This is the most IRQs you can get from one bus with this board, though.
+ */
+static int __init
+ev64260_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+       struct pci_controller   *hose = pci_bus_to_hose(dev->bus->number);
+
+       if (hose->index == 0) {
+               static char pci_irq_table[][4] =
+               /*
+                *      PCI IDSEL/INTPIN->INTLINE 
+                *         A   B   C   D
+                */
+               {
+                       {EV64260_PCI_0_IRQ,0,0,0}, /* IDSEL 7 - PCI bus 0 */
+                       {EV64260_PCI_0_IRQ,0,0,0}, /* IDSEL 8 - PCI bus 0 */
+               };
+
+               const long min_idsel = 7, max_idsel = 8, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       }
+       else {
+               static char pci_irq_table[][4] =
+               /*
+                *      PCI IDSEL/INTPIN->INTLINE 
+                *         A   B   C   D
+                */
+               {
+                       { EV64260_PCI_1_IRQ,0,0,0}, /* IDSEL 7 - PCI bus 1 */
+                       { EV64260_PCI_1_IRQ,0,0,0}, /* IDSEL 8 - PCI bus 1 */
+               };
+
+               const long min_idsel = 7, max_idsel = 8, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       }
+}
+
+static void __init
+ev64260_setup_peripherals(void)
+{
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN,
+               EV64260_EMB_FLASH_BASE, EV64260_EMB_FLASH_SIZE, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN,
+               EV64260_EXT_SRAM_BASE, EV64260_EXT_SRAM_SIZE, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN,
+               EV64260_TODC_BASE, EV64260_TODC_SIZE, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN,
+               EV64260_UART_BASE, EV64260_UART_SIZE, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN,
+               EV64260_EXT_FLASH_BASE, EV64260_EXT_FLASH_SIZE, 0);
+
+       TODC_INIT(TODC_TYPE_DS1501, 0, 0,
+                       ioremap(EV64260_TODC_BASE, EV64260_TODC_SIZE), 8);
+
+       mv64x60_clr_bits(&bh, MV64x60_CPU_CONFIG, ((1<<28) | (1<<29)));
+       mv64x60_set_bits(&bh, MV64x60_CPU_CONFIG, (1<<27));
+
+       if (ev64260_get_bus_speed() > 100000000) {
+               mv64x60_set_bits(&bh, MV64x60_CPU_CONFIG, (1<<23));
+       }
+
+       mv64x60_set_bits(&bh, MV64x60_PCI0_PCI_DECODE_CNTL,((1<<0) | (1<<3)));
+       mv64x60_set_bits(&bh, MV64x60_PCI1_PCI_DECODE_CNTL,((1<<0) | (1<<3)));
+
+        /*
+         * Enabling of PCI internal-vs-external arbitration
+         * is a platform- and errata-dependent decision.
+         */
+        if (bh.type == MV64x60_TYPE_GT64260A )  {
+                mv64x60_set_bits(&bh, MV64x60_PCI0_ARBITER_CNTL, (1<<31));
+                mv64x60_set_bits(&bh, MV64x60_PCI1_ARBITER_CNTL, (1<<31));
+        }
+
+        mv64x60_set_bits(&bh, MV64x60_CPU_MASTER_CNTL, (1<<9)); /* Only 1 cpu */
+
+       /*
+        * The EV-64260-BP uses several Multi-Purpose Pins (MPP) on the 64260
+        * bridge as interrupt inputs (via the General Purpose Ports (GPP)
+        * register).  Need to route the MPP inputs to the GPP and set the
+        * polarity correctly.
+        *
+        * In MPP Control 2 Register
+        *   MPP 21 -> GPP 21 (DUART channel A intr) bits 20-23 -> 0
+        *   MPP 22 -> GPP 22 (DUART channel B intr) bits 24-27 -> 0
+        */
+       mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_2, (0xf<<20) | (0xf<<24) );
+
+       /*
+        * In MPP Control 3 Register
+        *   MPP 26 -> GPP 26 (RTC INT)         bits  8-11 -> 0
+        *   MPP 27 -> GPP 27 (PCI 0 INTA)      bits 12-15 -> 0
+        *   MPP 29 -> GPP 29 (PCI 1 INTA)      bits 20-23 -> 0
+        */
+       mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_3,
+               (0xf<<8) | (0xf<<12) | (0xf<<20));
+
+#define GPP_EXTERNAL_INTERRUPTS \
+               ((1<<21) | (1<<22) | (1<<26) | (1<<27) | (1<<29))
+       /* DUART & PCI interrupts are inputs */
+       mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, GPP_EXTERNAL_INTERRUPTS);
+       /* DUART & PCI interrupts are active low */
+       mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, GPP_EXTERNAL_INTERRUPTS);
+
+       /* Clear any pending interrupts for these inputs and enable them. */
+       mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~GPP_EXTERNAL_INTERRUPTS);
+       mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, GPP_EXTERNAL_INTERRUPTS);
+
+       /*
+        * Set MPSC Multiplex RMII
+        * NOTE: ethernet driver modifies bit 0 and 1
+        */
+       mv64x60_write(&bh, GT64260_MPP_SERIAL_PORTS_MULTIPLEX, 0x00001102);
+       return;
+}
+
+
+static void __init
+ev64260_setup_bridge(void)
+{
+       mv64x60_setup_info_t    si;
+       int                     i;
+
+       memset(&si, 0, sizeof(si));
+
+       si.phys_reg_base = CONFIG_MV64X60_NEW_BASE;
+       si.map_irq = ev64260_map_irq;
+
+       si.pci_0.enable_bus = 1;
+       si.pci_0.enumerate_bus = 1;
+       si.pci_0.pci_io.cpu_base = 0xa0000000;
+       si.pci_0.pci_io.pci_base_hi = 0;
+       si.pci_0.pci_io.pci_base_lo = 0;
+       si.pci_0.pci_io.size = 0x01000000;
+       si.pci_0.pci_io.swap = 0x01000000; /* XXXX No swapping */
+       si.pci_0.pci_mem[0].cpu_base = 0x80000000;
+       si.pci_0.pci_mem[0].pci_base_hi = 0;
+       si.pci_0.pci_mem[0].pci_base_lo = 0x80000000;
+       si.pci_0.pci_mem[0].size = 0x10000000;
+       si.pci_0.pci_mem[0].swap = 0x01000000; /* XXXX No swapping */
+       si.pci_0.pci_mem[1].cpu_base = 0;
+       si.pci_0.pci_mem[1].pci_base_hi = 0;
+       si.pci_0.pci_mem[1].pci_base_lo = 0;
+       si.pci_0.pci_mem[1].size = 0;   /* Don't use this window */
+       si.pci_0.pci_mem[1].swap = 0;
+       si.pci_0.pci_mem[2].cpu_base = 0;
+       si.pci_0.pci_mem[2].pci_base_hi = 0;
+       si.pci_0.pci_mem[2].pci_base_lo = 0;
+       si.pci_0.pci_mem[2].size = 0;   /* Don't use this window */
+       si.pci_0.pci_mem[1].swap = 0;
+       si.pci_0.pci_cmd_bits = 0;
+       si.pci_0.latency_timer = 0x8;
+
+       si.pci_1.enable_bus = 1;
+       si.pci_1.enumerate_bus = 1;
+       si.pci_1.pci_io.cpu_base = 0xa1000000;
+       si.pci_1.pci_io.pci_base_hi = 0;
+       si.pci_1.pci_io.pci_base_lo = 0x01000000;
+       si.pci_1.pci_io.size = 0x01000000;
+       si.pci_1.pci_io.swap = 0x01000000; /* XXXX No swapping */
+       si.pci_1.pci_mem[0].cpu_base = 0x90000000;
+       si.pci_1.pci_mem[0].pci_base_hi = 0;
+       si.pci_1.pci_mem[0].pci_base_lo = 0x90000000;
+       si.pci_1.pci_mem[0].size = 0x10000000;
+       si.pci_1.pci_mem[0].swap = 0x01000000; /* XXXX No swapping */
+       si.pci_1.pci_mem[1].cpu_base = 0;
+       si.pci_1.pci_mem[1].pci_base_hi = 0;
+       si.pci_1.pci_mem[1].pci_base_lo = 0;
+       si.pci_1.pci_mem[1].size = 0;   /* Don't use this window */
+       si.pci_1.pci_mem[1].swap = 0;
+       si.pci_1.pci_mem[2].cpu_base = 0;
+       si.pci_1.pci_mem[2].pci_base_hi = 0;
+       si.pci_1.pci_mem[2].pci_base_lo = 0;
+       si.pci_1.pci_mem[2].size = 0;   /* Don't use this window */
+       si.pci_1.pci_mem[1].swap = 0;
+       si.pci_1.pci_cmd_bits = 0;
+       si.pci_1.latency_timer = 0x8;
+       si.pci_1.pci_cmd_bits = 0;
+       si.pci_1.latency_timer = 0x8;
+
+       for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) {
+               si.cpu_prot_options[i] = 0;
+               si.cpu_snoop_options[i] = GT64260_CPU_SNOOP_WB;
+               si.pci_0.acc_cntl_options[i] =
+                       /* Breaks PCI (especially slot 4)
+                       GT64260_PCI_ACC_CNTL_PREFETCHEN |
+                       */
+                       GT64260_PCI_ACC_CNTL_DREADEN |
+                       GT64260_PCI_ACC_CNTL_RDPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDMULPREFETCH |
+                       GT64260_PCI_ACC_CNTL_SWAP_NONE |
+                       GT64260_PCI_ACC_CNTL_MBURST_32_BTYES;
+               si.pci_0.snoop_options[i] = GT64260_PCI_SNOOP_WB;
+               si.pci_1.acc_cntl_options[i] =
+                       /* Breaks PCI (especially slot 4)
+                       GT64260_PCI_ACC_CNTL_PREFETCHEN |
+                       */
+                       GT64260_PCI_ACC_CNTL_DREADEN |
+                       GT64260_PCI_ACC_CNTL_RDPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDMULPREFETCH |
+                       GT64260_PCI_ACC_CNTL_SWAP_NONE |
+                       GT64260_PCI_ACC_CNTL_MBURST_32_BTYES;
+               si.pci_1.snoop_options[i] = GT64260_PCI_SNOOP_WB;
+       }
+
+        /* Lookup PCI host bridges */
+        if (mv64x60_init(&bh, &si)) {
+                printk("Bridge initialization failed.\n");
+        }
+
+       return;
+}
+
+#if defined(CONFIG_SERIAL_8250) && !defined(CONFIG_SERIAL_MPSC_CONSOLE)
+static void __init
+ev64260_early_serial_map(void)
+{
+       struct uart_port        port;
+       static char             first_time = 1;
+
+       if (first_time) {
+               memset(&port, 0, sizeof(port));
+
+               port.membase = ioremap(EV64260_SERIAL_0, EV64260_UART_SIZE);
+               port.irq = EV64260_UART_0_IRQ;
+               port.uartclk = BASE_BAUD * 16;
+               port.regshift = 2;
+               port.iotype = SERIAL_IO_MEM;
+               port.flags = STD_COM_FLAGS;
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+               gen550_init(0, &port);
+#endif
+
+               if (early_serial_setup(&port) != 0) {
+                       printk("Early serial init of port 0 failed\n");
+               }
+
+#if 0  /* XXXX */
+               /* Assume early_serial_setup() doesn't modify port */
+               port.membase = ioremap(EV64260_SERIAL_1, EV64260_UART_SIZE);
+               port.irq = EV64260_UART_1_IRQ;
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+               gen550_init(1, &port);
+#endif
+
+               if (early_serial_setup(&port) != 0) {
+                       printk("Early serial init of port 1 failed\n");
+               }
+#endif
+
+               first_time = 0;
+       }
+
+       return;
+}
+#elif defined(CONFIG_SERIAL_MPSC_CONSOLE)
+static void __init
+ev64260_early_serial_map(void)
+{
+#ifdef CONFIG_KGDB
+       static char     first_time = 1;
+
+
+#if defined(CONFIG_KGDB_TTYS0)
+#define KGDB_PORT 0
+#elif defined(CONFIG_KGDB_TTYS1)
+#define KGDB_PORT 1
+#else
+#error "Invalid kgdb_tty port"
+#endif
+
+       if (first_time) {
+               gt_early_mpsc_init(KGDB_PORT, B9600|CS8|CREAD|HUPCL|CLOCAL);
+               first_time = 0;
+       }
+
+       return;
+#endif
+}
+#endif
+
+static void __init
+ev64260_fixup_ocp(void)
+{
+#if defined(CONFIG_SERIAL_MPSC)
+       struct ocp_device       *dev;
+       mv64x60_ocp_mpsc_data_t *dp;
+
+       if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 0))
+                                                               != NULL) {
+               dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions;
+
+               dp->max_idle = 40;      /* XXXX what should this be? */
+               dp->default_baud = EV64260_DEFAULT_BAUD;
+               dp->brg_clk_src = EV64260_MPSC_CLK_SRC;
+               dp->brg_clk_freq = EV64260_MPSC_CLK_FREQ;
+       }
+
+       if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 1))
+                                                               != NULL) {
+               dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions;
+
+               dp->max_idle = 40;      /* XXXX what should this be? */
+               dp->default_baud = 9600;        /* XXXX */
+               dp->brg_clk_src = EV64260_MPSC_CLK_SRC;
+               dp->brg_clk_freq = EV64260_MPSC_CLK_FREQ;
+       }
+#endif
+
+       return;
+}
+
+static void __init
+ev64260_setup_arch(void)
+{
+       if ( ppc_md.progress )
+               ppc_md.progress("ev64260_setup_arch: enter", 0);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (initrd_start)
+               ROOT_DEV = Root_RAM0;
+       else
+#endif
+#ifdef CONFIG_ROOT_NFS
+               ROOT_DEV = Root_NFS;
+#else
+               ROOT_DEV = Root_SDA2;
+#endif
+
+       if ( ppc_md.progress )
+               ppc_md.progress("ev64260_setup_arch: Enabling L2 cache", 0);
+
+       /* Enable L2 and L3 caches (if 745x) */
+       _set_L2CR(_get_L2CR() | L2CR_L2E);
+       _set_L3CR(_get_L3CR() | L3CR_L3E);
+
+       if ( ppc_md.progress )
+               ppc_md.progress("ev64260_setup_arch: Initializing bridge", 0);
+
+       ev64260_setup_bridge();         /* set up PCI bridge(s) */
+       ev64260_setup_peripherals();    /* set up chip selects/GPP/MPP etc */
+
+       if ( ppc_md.progress )
+               ppc_md.progress("ev64260_setup_arch: bridge init complete", 0);
+
+       /* Set OCP values to reflect this board's setup */
+       ev64260_fixup_ocp();
+
+#ifdef CONFIG_DUMMY_CONSOLE
+       conswitchp = &dummy_con;
+#endif
+#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_MPSC_CONSOLE)
+       ev64260_early_serial_map();
+#endif
+
+       printk(BOARD_VENDOR " " BOARD_MACHINE "\n");
+       printk("EV-64260-BP port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n");
+
+       if ( ppc_md.progress )
+               ppc_md.progress("ev64260_setup_arch: exit", 0);
+
+       return;
+}
+
+static void
+ev64260_reset_board(void *addr)
+{
+       local_irq_disable();
+
+       /* disable and invalidate the L2 cache */
+       _set_L2CR(0);
+       _set_L2CR(0x200000);
+
+       /* flush and disable L1 I/D cache */
+       __asm__ __volatile__
+       ("mfspr   3,1008\n\t"
+        "ori   5,5,0xcc00\n\t"
+        "ori   4,3,0xc00\n\t"
+        "andc  5,3,5\n\t"
+        "sync\n\t"
+        "mtspr 1008,4\n\t"
+        "isync\n\t"
+        "sync\n\t"
+        "mtspr 1008,5\n\t"
+        "isync\n\t"
+        "sync\n\t");
+
+       /* unmap any other random cs's that might overlap with bootcs */
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, 0, 0, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, 0, 0, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, 0, 0, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN, 0, 0, 0);
+
+       /* map bootrom back in to gt @ reset defaults */
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN,
+                                               0xff800000, 8*1024*1024, 0);
+
+       /* move gt reg base back to default, setup default pci0 swapping
+        * config... */
+       mv64x60_write(&bh, MV64x60_INTERNAL_SPACE_DECODE,
+               (1<<24) | MV64x60_INTERNAL_SPACE_DEFAULT_ADDR>>20);
+
+       /* NOTE: FROM NOW ON no more GT_REGS accesses.. 0x1 is not mapped
+        * via BAT or MMU, and MSR IR/DR is ON */
+#if 0
+       /* BROKEN... IR/DR is still on !!  won't work!! */
+       /* Set exception prefix high - to the firmware */
+       _nmask_and_or_msr(0, MSR_IP);
+
+       out_8((u_char *)EV64260_BOARD_MODRST_REG, 0x01);
+#else
+       /* SRR0 has system reset vector, SRR1 has default MSR value */
+       /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */
+       /* NOTE: assumes reset vector is at 0xfff00100 */
+       __asm__ __volatile__
+       ("mtspr   26, %0\n\t"
+        "li      4,(1<<6)\n\t"
+        "mtspr   27,4\n\t"
+        "rfi\n\t"
+        :: "r" (addr):"r4");
+#endif
+       return;
+}
+
+static void
+ev64260_restart(char *cmd)
+{
+       volatile ulong  i = 10000000;
+
+       ev64260_reset_board((void *)0xfff00100);
+
+       while (i-- > 0);
+       panic("restart failed\n");
+}
+
+static void
+ev64260_halt(void)
+{
+       local_irq_disable();
+       while (1);
+       /* NOTREACHED */
+}
+
+static void
+ev64260_power_off(void)
+{
+       ev64260_halt();
+       /* NOTREACHED */
+}
+
+static int
+ev64260_show_cpuinfo(struct seq_file *m)
+{
+       uint pvid;
+
+       pvid = mfspr(PVR);
+       seq_printf(m, "vendor\t\t: " BOARD_VENDOR "\n");
+       seq_printf(m, "machine\t\t: " BOARD_MACHINE "\n");
+       seq_printf(m, "cpu MHz\t\t: %d\n", ev64260_get_cpu_speed()/1000/1000);
+       seq_printf(m, "bus MHz\t\t: %d\n", ev64260_get_bus_speed()/1000/1000);
+
+       return 0;
+}
+
+/* DS1501 RTC has too much variation to use RTC for calibration */
+static void __init
+ev64260_calibrate_decr(void)
+{
+       ulong freq;
+
+       freq = ev64260_get_bus_speed()/4;
+
+       printk("time_init: decrementer frequency = %lu.%.6lu MHz\n",
+              freq/1000000, freq%1000000);
+
+       tb_ticks_per_jiffy = freq / HZ;
+       tb_to_us = mulhwu_scale_factor(freq, 1000000);
+
+       return;
+}
+
+#if 0  /* XXXX */
+#ifdef CONFIG_USE_PPCBOOT
+static void parse_ppcbootinfo(unsigned long r3,
+       unsigned long r4, unsigned long r5,
+       unsigned long r6, unsigned long r7)
+{
+    bd_t *bd=NULL;
+    char *cmdline_start=NULL;
+    int cmdline_len=0;
+
+    if(r3) {
+       if((r3 & 0xf0000000) == 0) r3 += KERNELBASE;
+       if((r3 & 0xf0000000) == KERNELBASE) {
+           bd=(void *)r3;
+
+           /* hack for ppcboot loaders that report freqs in Mhz */
+           if(bd->bi_intfreq<1000000) bd->bi_intfreq*=1000000;
+           if(bd->bi_busfreq<1000000) bd->bi_busfreq*=1000000;
+
+           memcpy(&ppcboot_bd,bd,sizeof(ppcboot_bd));
+           ppcboot_bd_valid=1;
+       }
+    }
+
+#ifdef CONFIG_BLK_DEV_INITRD
+    if(r4 && r5 && r5>r4) {
+       if((r4 & 0xf0000000) == 0) r4 += KERNELBASE;
+       if((r5 & 0xf0000000) == 0) r5 += KERNELBASE;
+       if((r4 & 0xf0000000) == KERNELBASE) {
+           initrd_start=r4;
+           initrd_end=r5;
+           initrd_below_start_ok = 1;
+       }
+    }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+    if(r6 && r7 && r7>r6) {
+       if((r6 & 0xf0000000) == 0) r6 += KERNELBASE;
+       if((r7 & 0xf0000000) == 0) r7 += KERNELBASE;
+       if((r6 & 0xf0000000) == KERNELBASE) {
+           cmdline_start=(void *)r6;
+           cmdline_len=(r7-r6);
+           strncpy(cmd_line,cmdline_start,cmdline_len);
+       }
+    }
+
+    if(ppcboot_bd_valid) {                     
+       printk("found bd_t @%p\n", bd);
+       printk("memstart=%08lx\n", bd->bi_memstart);
+       printk("memsize=%08lx\n", bd->bi_memsize);
+       printk("enetaddr=%02x%02x%02x%02x%02x%02x\n",
+               bd->bi_enetaddr[0],
+               bd->bi_enetaddr[1],
+               bd->bi_enetaddr[2],
+               bd->bi_enetaddr[3],
+               bd->bi_enetaddr[4],
+               bd->bi_enetaddr[5]
+               );
+       printk("intfreq=%ld\n", bd->bi_intfreq);
+       printk("busfreq=%ld\n", bd->bi_busfreq);
+       printk("baudrate=%ld\n", bd->bi_baudrate);
+    }
+
+#ifdef CONFIG_BLK_DEV_INITRD
+    if(initrd_start) {
+       printk("found initrd @%lx-%lx\n", initrd_start, initrd_end);
+    }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+    if(cmdline_start && cmdline_len) {
+       printk("found cmdline: '%s'\n", cmd_line);
+    }
+}
+#endif /* USE PPC_BOOT */
+#endif
+
+#if 0  /* XXXX */
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+static int
+ev64260_ide_check_region(ide_ioreg_t from, unsigned int extent)
+{
+        return check_region(from, extent);
+}
+
+static void
+ev64260_ide_request_region(ide_ioreg_t from,
+                        unsigned int extent,
+                        const char *name)
+{
+        request_region(from, extent, name);
+       return;
+}
+
+static void
+ev64260_ide_release_region(ide_ioreg_t from,
+                        unsigned int extent)
+{
+        release_region(from, extent);
+       return;
+}
+
+static void __init
+ev64260_ide_pci_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port,
+                ide_ioreg_t ctrl_port, int *irq)
+{
+       struct pci_dev  *dev;
+#if 1 /* NTL */
+        int i;
+
+       //printk("regs %d to %d @ 0x%x\n", IDE_DATA_OFFSET, IDE_STATUS_OFFSET, data_port);
+        for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+                hw->io_ports[i] = data_port;
+                data_port++;
+        }
+
+       //printk("ctrl %d @ 0x%x\n", IDE_CONTROL_OFFSET, ctrl_port);
+        hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+#endif
+
+       pci_for_each_dev(dev) {
+               if (((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) ||
+                   ((dev->class >> 8) == PCI_CLASS_STORAGE_RAID)) {
+                       hw->irq = dev->irq;
+
+                       if (irq != NULL) {
+                               *irq = dev->irq;
+                       }
+               }
+       }
+
+       return;
+}
+#endif
+#endif
+
+#if !defined(CONFIG_USE_PPCBOOT)
+/*
+ * Set BAT 3 to map 0xfb000000 to 0xfc000000 of physical memory space.
+ */
+static __inline__ void
+ev64260_set_bat(void)
+{
+       mb();
+       mtspr(DBAT1U, 0xfb0001fe);
+       mtspr(DBAT1L, 0xfb00002a);
+       mb();
+
+       return;
+}
+#endif
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+static void __init
+ev64260_map_io(void)
+{
+       io_block_mapping(0xfb000000, 0xfb000000, 0x01000000, _PAGE_IO);
+}
+#endif
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+             unsigned long r6, unsigned long r7)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+       extern int      initrd_below_start_ok;
+
+       initrd_start=initrd_end=0;
+       initrd_below_start_ok=0;
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+       ppcboot_bd_valid=0;
+       memset(&ppcboot_bd,0,sizeof(ppcboot_bd));
+
+#ifdef CONFIG_USE_PPCBOOT
+       parse_ppcbootinfo(r3, r4, r5, r6, r7);
+#else
+       parse_bootinfo(find_bootinfo());
+#endif
+
+       isa_mem_base = 0;
+       isa_io_base = 0xa0000000;       /* XXXX */
+       pci_dram_offset = 0x80000000;   /* XXXX */
+
+       loops_per_jiffy = ev64260_get_cpu_speed() / HZ;
+
+       ppc_md.setup_arch = ev64260_setup_arch;
+       ppc_md.show_cpuinfo = ev64260_show_cpuinfo;
+       ppc_md.init_IRQ = gt64260_init_irq;
+       ppc_md.get_irq = gt64260_get_irq;
+
+       ppc_md.pcibios_fixup = ev64260_pci_fixups;
+
+       ppc_md.restart = ev64260_restart;
+       ppc_md.power_off = ev64260_power_off;
+       ppc_md.halt = ev64260_halt;
+
+       ppc_md.find_end_of_memory = ev64260_find_end_of_memory;
+
+       ppc_md.init = NULL;
+
+       ppc_md.time_init = todc_time_init;
+       ppc_md.set_rtc_time = todc_set_rtc_time;
+       ppc_md.get_rtc_time = todc_get_rtc_time;
+
+       ppc_md.nvram_read_val = todc_direct_read_val;
+       ppc_md.nvram_write_val = todc_direct_write_val;
+
+       ppc_md.calibrate_decr = ev64260_calibrate_decr;
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+        ppc_ide_md.ide_init_hwif = ev64260_ide_pci_init_hwif_ports;
+#endif
+
+       bh.p_base = CONFIG_MV64X60_NEW_BASE;
+
+#if !defined(CONFIG_USE_PPCBOOT)
+       ev64260_set_bat();
+#endif
+
+#ifdef CONFIG_SERIAL_8250
+#if defined(CONFIG_SERIAL_TEXT_DEBUG)
+       ppc_md.setup_io_mappings = ev64260_map_io;
+       ppc_md.progress = gen550_progress;
+#endif
+#if defined(CONFIG_KGDB)
+       ppc_md.setup_io_mappings = ev64260_map_io;
+       ppc_md.early_serial_map = ev64260_early_serial_map;
+#endif
+#elif defined(CONFIG_SERIAL_MPSC_CONSOLE)
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+       ppc_md.setup_io_mappings = ev64260_map_io;
+       ppc_md.progress = gt64260_mpsc_progress;
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
+#ifdef CONFIG_KGDB
+       ppc_md.setup_io_mappings = ev64260_map_io;
+       ppc_md.early_serial_map = ev64260_early_serial_map;
+#endif /* CONFIG_KGDB */
+
+#endif
+
+       return;
+}
diff --git a/arch/ppc/syslib/mv64360_pic.c b/arch/ppc/syslib/mv64360_pic.c
new file mode 100644 (file)
index 0000000..ef07629
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * arch/ppc/kernel/mv64360_pic.c
+ * 
+ * Interrupt controller support for Marvell's MV64360.
+ *
+ * Author: Rabeeh Khoury <rabeeh@galileo.co.il>
+ * Based on MV64360 PIC written by
+ * Chris Zankel <chris@mvista.com>
+ * Mark A. Greer <mgreer@mvista.com>
+ *
+ * Copyright 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+/*
+ * This file contains the specific functions to support the MV64360
+ * interrupt controller.
+ *
+ * The MV64360 has two main interrupt registers (high and low) that
+ * summarizes the interrupts generated by the units of the MV64360.
+ * Each bit is assigned to an interrupt number, where the low register
+ * are assigned from IRQ0 to IRQ31 and the high cause register
+ * from IRQ32 to IRQ63 
+ * The GPP (General Purpose Pins) interrupts are assigned from IRQ64 (GPP0) 
+ * to IRQ95 (GPP31). 
+ * get_irq() returns the lowest interrupt number that is currently asserted.
+ *
+ * Note: 
+ *  - This driver does not initialize the GPP when used as an interrupt 
+ *    input.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stddef.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/ocp.h>
+#include <asm/mv64x60.h>
+
+#ifdef CONFIG_IRQ_ALL_CPUS
+#error "The mv64360 does not support yet distribution of IRQs on all CPUs"
+#endif
+/* ========================== forward declaration ========================== */
+
+static void mv64360_unmask_irq(unsigned int);
+static void mv64360_mask_irq(unsigned int);
+static irqreturn_t mv64360_cpu_error_int_handler(int, void *, struct pt_regs *);
+static irqreturn_t mv64360_sram_error_int_handler(int, void *, struct pt_regs *);
+static irqreturn_t mv64360_pci_error_int_handler(int, void *, struct pt_regs *);
+
+/* ========================== local declarations =========================== */
+
+struct hw_interrupt_type mv64360_pic = {
+       .typename = " MV64360_PIC ",    /* typename */
+       .enable = mv64360_unmask_irq,   /* enable */
+       .disable = mv64360_mask_irq,    /* disable */
+       .ack = mv64360_mask_irq,        /* ack */
+};
+
+#define CPU_INTR_STR   "MV64360 CPU interface error"
+#define SRAM_INTR_STR  "MV64360 internal sram error"
+#define PCI0_INTR_STR  "MV64360 PCI 0 error"
+#define PCI1_INTR_STR  "MV64360 PCI 1 error"
+
+static mv64x60_handle_t        base_bh;
+
+u32 mv64360_irq_base = 0;      /* MV64360 handles the next 96 IRQs from here */
+
+/* mv64360_init_irq()
+ *
+ *  This function initializes the interrupt controller. It assigns
+ *  all interrupts from IRQ0 to IRQ95 to the mv64360 interrupt controller.
+ *
+ * Input Variable(s):
+ *  None.
+ *
+ * Outpu. Variable(s):
+ *  None.
+ *
+ * Returns:
+ *  void
+ *
+ * Note:
+ *  We register all GPP inputs as interrupt source, but disable them.
+ */
+
+__init void
+mv64360_init_irq(void)
+{
+       struct ocp_def  *def;
+       int i;
+
+       if (ppc_md.progress)
+               ppc_md.progress("mv64360_init_irq: enter", 0x0);
+
+       if ( ppc_md.progress ) ppc_md.progress("mv64360_init_irq: enter", 0x0);
+
+       if ((def = ocp_get_one_device(OCP_VENDOR_MARVELL, OCP_FUNC_HB,
+                                       OCP_ANY_INDEX)) == NULL) {
+               /* XXXX SCREAM */
+               return;
+       }
+       base_bh.v_base = (unsigned long)ioremap(def->paddr, 0x1000);
+
+       ppc_cached_irq_mask[0] = 0;
+       ppc_cached_irq_mask[1] = 0x0f000000;    /* Enable GPP intrs */
+       ppc_cached_irq_mask[2] = 0;
+
+       /* disable all interrupts and clear current interrupts */
+       mv64x60_write(&base_bh, MV64x60_GPP_INTR_CAUSE, 0);
+       mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK,
+               ppc_cached_irq_mask[2]);
+       mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_LO,
+               ppc_cached_irq_mask[0]);
+       mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_HI,
+               ppc_cached_irq_mask[1]);
+
+       /* use the mv64360 for all (possible) interrupt sources */
+       for (i = mv64360_irq_base; i < (mv64360_irq_base + 96); i++) {
+               /* All interrupts are level interrupts */
+               irq_desc[i].status |= IRQ_LEVEL;
+               irq_desc[i].handler = &mv64360_pic;
+       }
+
+       /* Register CPU interface error interrupt handler */
+       request_irq(MV64x60_IRQ_CPU_ERR, mv64360_cpu_error_int_handler,
+               SA_INTERRUPT, CPU_INTR_STR, 0);
+       mv64x60_write(&base_bh, MV64x60_CPU_ERR_MASK, 0x000000ff);
+
+       /* Register internal SRAM error interrupt handler */
+       request_irq(MV64360_IRQ_SRAM_PAR_ERR, mv64360_sram_error_int_handler,
+               SA_INTERRUPT, SRAM_INTR_STR, 0);
+
+       /* Register PCI 0 error interrupt handler */
+       request_irq(MV64360_IRQ_PCI0, mv64360_pci_error_int_handler,
+               SA_INTERRUPT, PCI0_INTR_STR, (void *) 0);
+       mv64x60_write(&base_bh, MV64x60_PCI0_ERR_MASK, 0x00a50c25);
+
+       /* Register PCI 1 error interrupt handler */
+       request_irq(MV64360_IRQ_PCI1, mv64360_pci_error_int_handler,
+               SA_INTERRUPT, PCI1_INTR_STR, (void *) 1);
+       mv64x60_write(&base_bh, MV64x60_PCI1_ERR_MASK, 0x00a50c25);
+
+       if (ppc_md.progress)
+               ppc_md.progress("mv64360_init_irq: exit", 0x0);
+}
+
+
+/* mv64360_get_irq()
+ *
+ *  This function returns the lowest interrupt number of all interrupts that
+ *  are currently asserted. 
+ *
+ * Input Variable(s):
+ *  struct pt_regs*    not used
+ *
+ * Output Variable(s):
+ *  None.
+ *
+ * Returns:
+ *  int        <interrupt number> or -2 (bogus interrupt)
+ * 
+ */
+int
+mv64360_get_irq(struct pt_regs *regs)
+{
+       int irq;
+       int irq_gpp;
+
+#ifdef CONFIG_SMP
+#define BIT28 (1<<28)
+       /* 
+        * Second CPU gets only doorbell (message) interrupts.
+        * The doorbell interrupt is BIT28 in the main interrupt low cause reg.
+        */
+       int cpu_nr = smp_processor_id();
+       if (cpu_nr == 1) {
+               irq = mv64x60_read(&base_bh, MV64360_IC_MAIN_CAUSE_LO);
+               if (!(irq & BIT28))
+                       return -1;
+               return 28;
+       }
+#endif
+
+       irq = mv64x60_read(&base_bh, MV64360_IC_MAIN_CAUSE_LO);
+       irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]);
+       if (irq == -1) {
+               irq = mv64x60_read(&base_bh, MV64360_IC_MAIN_CAUSE_HI);
+               irq = __ilog2((irq & 0x1f0003f7) & ppc_cached_irq_mask[1]);
+               if (irq == -1) {
+                       irq = -2;       /* bogus interrupt, should never happen */
+               } else {
+                       if ((irq >= 24) && (irq < 28)) {
+                               irq_gpp =
+                                       mv64x60_read(&base_bh,
+                                               MV64x60_GPP_INTR_CAUSE);
+                               irq_gpp =
+                                       __ilog2(irq_gpp &
+                                       ppc_cached_irq_mask[2]);
+
+                               if (irq_gpp == -1) {
+                                       irq = -2;
+                               } else {
+                                       irq = irq_gpp + 64;
+                                       mv64x60_write(&base_bh,
+                                               MV64x60_GPP_INTR_CAUSE,
+                                               ~(1 << (irq - 64)));
+                               }
+                       } else {
+                               irq += 32;
+                       }
+               }
+       }
+
+       if (irq < 0) {
+               return (irq);
+       } else {
+               return (mv64360_irq_base + irq);
+       }
+}
+
+/* mv64360_unmask_irq()
+ *
+ *  This function enables an interrupt.
+ *
+ * Input Variable(s):
+ *  unsigned int       interrupt number (IRQ0...IRQ95).
+ *
+ * Output Variable(s):
+ *  None.
+ *
+ * Returns:
+ *  void
+ */
+
+static void
+mv64360_unmask_irq(unsigned int irq)
+{
+#ifdef CONFIG_SMP
+       /* second CPU gets only doorbell interrupts */
+       if ((irq - mv64360_irq_base) == 28) {
+               mv64x60_set_bits(&base_bh, MV64360_IC_CPU1_INTR_MASK_LO, BIT28);
+               return;
+       }
+#endif
+       irq -= mv64360_irq_base;
+       if (irq > 31) {
+               if (irq > 63) {
+                       /* unmask GPP irq */
+                       mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK,
+                               ppc_cached_irq_mask[2] |= (1 << (irq - 64)));
+               } else {
+                       /* mask high interrupt register */
+                       mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_HI,
+                               ppc_cached_irq_mask[1] |= (1 << (irq - 32)));
+               }
+       } else {
+               /* mask low interrupt register */
+               mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_LO,
+                       ppc_cached_irq_mask[0] |= (1 << irq));
+       }
+}
+
+
+/* mv64360_mask_irq()
+ *
+ *  This function disables the requested interrupt.
+ *
+ * Input Variable(s):
+ *  unsigned int       interrupt number (IRQ0...IRQ95).
+ *
+ * Output Variable(s):
+ *  None.
+ *
+ * Returns:
+ *  void
+ */
+
+static void
+mv64360_mask_irq(unsigned int irq)
+{
+#ifdef CONFIG_SMP
+       if ((irq - mv64360_irq_base) == 28) {
+               mv64x60_clr_bits(&base_bh, MV64360_IC_CPU1_INTR_MASK_LO, BIT28);
+               return;
+       }
+#endif
+       irq -= mv64360_irq_base;
+       if (irq > 31) {
+               if (irq > 63) {
+                       /* mask GPP irq */
+                       mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK,
+                               ppc_cached_irq_mask[2] &= ~(1 << (irq - 64)));
+               } else {
+                       /* mask high interrupt register */
+                       mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_HI,
+                               ppc_cached_irq_mask[1] &= ~(1 << (irq - 32)));
+               }
+       } else {
+               /* mask low interrupt register */
+               mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_LO,
+                       ppc_cached_irq_mask[0] &= ~(1 << irq));
+       }
+
+}
+
+static irqreturn_t
+mv64360_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       u32 val;
+       val = mv64x60_read(&base_bh, MV64x60_CPU_ERR_CAUSE);
+       printk(KERN_ERR
+               "mv64360_cpu_error_int_handler: Error on CPU interface - Cause regiser 0x%08x\n",
+               val);
+       printk(KERN_ERR "\tCPU error register dump:\n");
+       printk(KERN_ERR "\tAddress low  0x%08x\n",
+               mv64x60_read(&base_bh, MV64x60_CPU_ERR_ADDR_LO));
+       printk(KERN_ERR "\tAddress high 0x%08x\n",
+               mv64x60_read(&base_bh, MV64x60_CPU_ERR_ADDR_HI));
+       printk(KERN_ERR "\tData low     0x%08x\n",
+               mv64x60_read(&base_bh, MV64x60_CPU_ERR_DATA_LO));
+       printk(KERN_ERR "\tData high    0x%08x\n",
+               mv64x60_read(&base_bh, MV64x60_CPU_ERR_DATA_HI));
+       printk(KERN_ERR "\tParity       0x%08x\n",
+               mv64x60_read(&base_bh, MV64x60_CPU_ERR_PARITY));
+       mv64x60_write(&base_bh, MV64x60_CPU_ERR_CAUSE, 0);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+mv64360_sram_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       printk(KERN_ERR
+               "mv64360_sram_error_int_handler: Error in internal SRAM - Cause register 0x%08x\n",
+               mv64x60_read(&base_bh, MV64360_SRAM_ERR_CAUSE));
+       printk(KERN_ERR "\tSRAM error register dump:\n");
+       printk(KERN_ERR "\tAddress Low  0x%08x\n",
+               mv64x60_read(&base_bh, MV64360_SRAM_ERR_ADDR_LO));
+       printk(KERN_ERR "\tAddress High 0x%08x\n",
+               mv64x60_read(&base_bh, MV64360_SRAM_ERR_ADDR_HI));
+       printk(KERN_ERR "\tData Low     0x%08x\n",
+               mv64x60_read(&base_bh, MV64360_SRAM_ERR_DATA_LO));
+       printk(KERN_ERR "\tData High    0x%08x\n",
+               mv64x60_read(&base_bh, MV64360_SRAM_ERR_DATA_HI));
+       printk(KERN_ERR "\tParity       0x%08x\n",
+               mv64x60_read(&base_bh, MV64360_SRAM_ERR_PARITY));
+       mv64x60_write(&base_bh, MV64360_SRAM_ERR_CAUSE, 0);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+mv64360_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       u32 val;
+       unsigned int pci_bus = (unsigned int) dev_id;
+       if (pci_bus == 0) {     /* Error on PCI 0 */
+               val = mv64x60_read(&base_bh, MV64x60_PCI0_ERR_CAUSE);
+               printk(KERN_ERR
+                       "mv64360_pci_error_int_handler: Error in PCI %d Interface\n",
+                       pci_bus);
+               printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
+               printk(KERN_ERR "\tCause register 0x%08x\n", val);
+               printk(KERN_ERR "\tAddress Low    0x%08x\n",
+                       mv64x60_read(&base_bh, MV64x60_PCI0_ERR_ADDR_LO));
+               printk(KERN_ERR "\tAddress High   0x%08x\n",
+                       mv64x60_read(&base_bh, MV64x60_PCI0_ERR_ADDR_HI));
+               printk(KERN_ERR "\tAttribute      0x%08x\n",
+                       mv64x60_read(&base_bh, MV64x60_PCI0_ERR_DATA_LO));
+               printk(KERN_ERR "\tCommand        0x%08x\n",
+                       mv64x60_read(&base_bh, MV64x60_PCI0_ERR_CMD));
+               mv64x60_write(&base_bh, MV64x60_PCI0_ERR_CAUSE, ~val);
+       }
+       if (pci_bus == 1) {     /* Error on PCI 1 */
+               val = mv64x60_read(&base_bh, MV64x60_PCI1_ERR_CAUSE);
+               printk(KERN_ERR
+                       "mv64360_pci_error_int_handler: Error in PCI %d Interface\n",
+                       pci_bus);
+               printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
+               printk(KERN_ERR "\tCause register 0x%08x\n", val);
+               printk(KERN_ERR "\tAddress Low    0x%08x\n",
+                       mv64x60_read(&base_bh, MV64x60_PCI1_ERR_ADDR_LO));
+               printk(KERN_ERR "\tAddress High   0x%08x\n",
+                       mv64x60_read(&base_bh, MV64x60_PCI1_ERR_ADDR_HI));
+               printk(KERN_ERR "\tAttribute      0x%08x\n",
+                       mv64x60_read(&base_bh, MV64x60_PCI1_ERR_DATA_LO));
+               printk(KERN_ERR "\tCommand        0x%08x\n",
+                       mv64x60_read(&base_bh, MV64x60_PCI1_ERR_CMD));
+               mv64x60_write(&base_bh, MV64x60_PCI1_ERR_CAUSE, ~val);
+       }
+       return IRQ_HANDLED;
+}
diff --git a/arch/ppc/syslib/mv64x60.c b/arch/ppc/syslib/mv64x60.c
new file mode 100644 (file)
index 0000000..c31fe53
--- /dev/null
@@ -0,0 +1,2872 @@
+/*
+ * arch/ppc/syslib/mv64x60.c
+ * 
+ * Common routines for the Marvell/Galileo Discovery line of host bridges
+ * (e.g, gt64260 and mv64360).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *        Rabeeh Khoury <rabeeh@galileo.co.il>
+ *
+ * 2001-2002 (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/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/mv64x60.h>
+#include <asm/delay.h>
+#include <asm/ocp.h>
+
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define        DBG(x...) printk(x)
+#else
+#define        DBG(x...)
+#endif /* DEBUG */
+
+
+static u32 mv64x60_mask(u32 val, u32 num_bits);
+static u32 mv64x60_shift_left(u32 val, u32 num_bits);
+static u32 mv64x60_shift_right(u32 val, u32 num_bits);
+static void mv64x60_early_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si);
+static int mv64x60_get_type(mv64x60_handle_t *bh);
+static int mv64x60_setup_for_chip(mv64x60_handle_t *bh);
+static void mv64x60_get_mem_windows(mv64x60_handle_t *bh,
+       u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]);
+static u32 mv64x60_calc_mem_size(mv64x60_handle_t *bh,
+       u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]);
+static void mv64x60_config_cpu2mem_windows(mv64x60_handle_t *bh,
+       mv64x60_setup_info_t *si, u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]);
+static void mv64x60_config_cpu2pci_windows(mv64x60_handle_t *bh,
+       mv64x60_setup_info_t *si);
+static void mv64x60_set_cpu2pci_window(mv64x60_handle_t *bh,
+       mv64x60_pci_info_t *pi, u32 *win_tab, u32 *remap_tab);
+static void mv64x60_config_pci2mem_windows(mv64x60_handle_t *bh,
+       mv64x60_setup_info_t *si, u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]);
+static void mv64x60_alloc_hoses(mv64x60_handle_t *bh, mv64x60_setup_info_t *si);
+static void mv64x60_init_hoses(mv64x60_handle_t *bh, mv64x60_setup_info_t *si);
+static void mv64x60_init_resources(struct pci_controller *hose,
+       mv64x60_pci_info_t *pi, u32 io_base);
+static void mv64x60_set_pci_params(struct pci_controller *hose,
+       mv64x60_pci_info_t *pi);
+static void mv64x60_enumerate_buses(mv64x60_handle_t *bh,
+       mv64x60_setup_info_t *si);
+static int mv64x60_pci_exclude_device(u8 bus, u8 devfn);
+static void mv64x60_fixup_ocp(struct ocp_device *, void *arg);
+
+static u32 gt64260_translate_size(u32 base, u32 size, u32 num_bits);
+static u32 gt64260_untranslate_size(u32 base, u32 size, u32 num_bits);
+static void gt64260_set_pci2mem_window(struct pci_controller *hose,
+       u32 window, u32 base);
+static u32 gt64260_is_enabled_32bit(mv64x60_handle_t *bh, u32 window);
+static void gt64260_enable_window_32bit(mv64x60_handle_t *bh, u32 window);
+static void gt64260_disable_window_32bit(mv64x60_handle_t *bh, u32 window);
+static void gt64260_enable_window_64bit(mv64x60_handle_t *bh, u32 window);
+static void gt64260_disable_window_64bit(mv64x60_handle_t *bh, u32 window);
+static void gt64260_disable_all_windows(mv64x60_handle_t *bh,
+                                       mv64x60_setup_info_t *si);
+static void gt64260a_chip_specific_init(mv64x60_handle_t *bh,
+       mv64x60_setup_info_t *si);
+static void gt64260b_chip_specific_init(mv64x60_handle_t *bh,
+       mv64x60_setup_info_t *si);
+
+static u32 mv64360_translate_size(u32 base_addr, u32 size, u32 num_bits);
+static u32 mv64360_untranslate_size(u32 base_addr, u32 size, u32 num_bits);
+static void mv64360_set_pci2mem_window(struct pci_controller *hose,
+       u32 window, u32 base);
+static u32 mv64360_is_enabled_32bit(mv64x60_handle_t *bh, u32 window);
+static void mv64360_enable_window_32bit(mv64x60_handle_t *bh, u32 window);
+static void mv64360_disable_window_32bit(mv64x60_handle_t *bh, u32 window);
+static void mv64360_enable_window_64bit(mv64x60_handle_t *bh, u32 window);
+static void mv64360_disable_window_64bit(mv64x60_handle_t *bh, u32 window);
+static void mv64360_disable_all_windows(mv64x60_handle_t *bh,
+                                       mv64x60_setup_info_t *si);
+static void mv64360_chip_specific_init(mv64x60_handle_t *bh,
+       mv64x60_setup_info_t *si);
+static void mv64460_chip_specific_init(mv64x60_handle_t *bh,
+       mv64x60_setup_info_t *si);
+
+
+u8     mv64x60_pci_exclude_bridge = TRUE;
+
+spinlock_t mv64x60_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t mv64x60_rmw_lock = SPIN_LOCK_UNLOCKED;
+
+static mv64x60_32bit_window_t gt64260_32bit_windows[] __initdata = {
+       /* CPU->MEM Windows */
+       [MV64x60_CPU2MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2MEM_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2MEM_1_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2MEM_2_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2MEM_3_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->Device Windows */
+       [MV64x60_CPU2DEV_0_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_0_BASE,
+               .size_reg               = MV64x60_CPU2DEV_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2DEV_1_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_1_BASE,
+               .size_reg               = MV64x60_CPU2DEV_1_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2DEV_2_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_2_BASE,
+               .size_reg               = MV64x60_CPU2DEV_2_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2DEV_3_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_3_BASE,
+               .size_reg               = MV64x60_CPU2DEV_3_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->Boot Window */
+       [MV64x60_CPU2BOOT_WIN] = {
+               .base_reg               = MV64x60_CPU2BOOT_0_BASE,
+               .size_reg               = MV64x60_CPU2BOOT_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 0 Windows */
+       [MV64x60_CPU2PCI0_IO_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_IO_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_IO_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_1_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_2_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_3_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 Windows */
+       [MV64x60_CPU2PCI1_IO_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_IO_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_IO_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_1_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_2_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_3_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->SRAM Window (64260 has no integrated SRAM) */
+       /* CPU->PCI 0 Remap I/O Window */
+       [MV64x60_CPU2PCI0_IO_REMAP_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_IO_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 Remap I/O Window */
+       [MV64x60_CPU2PCI1_IO_REMAP_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_IO_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU Memory Protection Windows */
+       [MV64x60_CPU_PROT_0_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_0,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_0,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_PROT_1_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_1,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_1,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_PROT_2_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_2,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_2,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_PROT_3_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_3,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_3,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU Snoop Windows */
+       [MV64x60_CPU_SNOOP_0_WIN] = {
+               .base_reg               = GT64260_CPU_SNOOP_BASE_0,
+               .size_reg               = GT64260_CPU_SNOOP_SIZE_0,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_SNOOP_1_WIN] = {
+               .base_reg               = GT64260_CPU_SNOOP_BASE_1,
+               .size_reg               = GT64260_CPU_SNOOP_SIZE_1,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_SNOOP_2_WIN] = {
+               .base_reg               = GT64260_CPU_SNOOP_BASE_2,
+               .size_reg               = GT64260_CPU_SNOOP_SIZE_2,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_SNOOP_3_WIN] = {
+               .base_reg               = GT64260_CPU_SNOOP_BASE_3,
+               .size_reg               = GT64260_CPU_SNOOP_SIZE_3,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 0->System Memory Remap Windows */
+       [MV64x60_PCI02MEM_REMAP_0_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_0_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_1_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_2_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_3_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       /* PCI 1->System Memory Remap Windows */
+       [MV64x60_PCI12MEM_REMAP_0_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_0_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_1_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_2_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_3_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+};
+
+static mv64x60_64bit_window_t gt64260_64bit_windows[] __initdata = {
+       /* CPU->PCI 0 MEM Remap Windows */
+       [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_0_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_0_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_1_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_1_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_2_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_2_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_3_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_3_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 MEM Remap Windows */
+       [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_0_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_0_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_1_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_1_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_2_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_2_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_3_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_3_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 0->MEM Access Control Windows */
+       [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_0_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_0_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_0_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_1_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_1_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_1_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_2_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_2_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_2_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_3_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_3_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_3_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 1->MEM Access Control Windows */
+       [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_0_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_0_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_0_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_1_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_1_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_1_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_2_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_2_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_2_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_3_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_3_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_3_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 0->MEM Snoop Windows */
+       [MV64x60_PCI02MEM_SNOOP_0_WIN] = {
+               .base_hi_reg            = GT64260_PCI0_SNOOP_0_BASE_HI,
+               .base_lo_reg            = GT64260_PCI0_SNOOP_0_BASE_LO,
+               .size_reg               = GT64260_PCI0_SNOOP_0_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_SNOOP_1_WIN] = {
+               .base_hi_reg            = GT64260_PCI0_SNOOP_1_BASE_HI,
+               .base_lo_reg            = GT64260_PCI0_SNOOP_1_BASE_LO,
+               .size_reg               = GT64260_PCI0_SNOOP_1_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_SNOOP_2_WIN] = {
+               .base_hi_reg            = GT64260_PCI0_SNOOP_2_BASE_HI,
+               .base_lo_reg            = GT64260_PCI0_SNOOP_2_BASE_LO,
+               .size_reg               = GT64260_PCI0_SNOOP_2_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_SNOOP_3_WIN] = {
+               .base_hi_reg            = GT64260_PCI0_SNOOP_3_BASE_HI,
+               .base_lo_reg            = GT64260_PCI0_SNOOP_3_BASE_LO,
+               .size_reg               = GT64260_PCI0_SNOOP_3_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 1->MEM Snoop Windows */
+       [MV64x60_PCI12MEM_SNOOP_0_WIN] = {
+               .base_hi_reg            = GT64260_PCI1_SNOOP_0_BASE_HI,
+               .base_lo_reg            = GT64260_PCI1_SNOOP_0_BASE_LO,
+               .size_reg               = GT64260_PCI1_SNOOP_0_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_SNOOP_1_WIN] = {
+               .base_hi_reg            = GT64260_PCI1_SNOOP_1_BASE_HI,
+               .base_lo_reg            = GT64260_PCI1_SNOOP_1_BASE_LO,
+               .size_reg               = GT64260_PCI1_SNOOP_1_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_SNOOP_2_WIN] = {
+               .base_hi_reg            = GT64260_PCI1_SNOOP_2_BASE_HI,
+               .base_lo_reg            = GT64260_PCI1_SNOOP_2_BASE_LO,
+               .size_reg               = GT64260_PCI1_SNOOP_2_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_SNOOP_3_WIN] = {
+               .base_hi_reg            = GT64260_PCI1_SNOOP_3_BASE_HI,
+               .base_lo_reg            = GT64260_PCI1_SNOOP_3_BASE_LO,
+               .size_reg               = GT64260_PCI1_SNOOP_3_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+};
+
+static mv64x60_chip_info_t gt64260a_ci __initdata = {
+       .translate_size         = gt64260_translate_size,
+       .untranslate_size       = gt64260_untranslate_size,
+       .set_pci2mem_window     = gt64260_set_pci2mem_window,
+       .is_enabled_32bit       = gt64260_is_enabled_32bit,
+       .enable_window_32bit    = gt64260_enable_window_32bit,
+       .disable_window_32bit   = gt64260_disable_window_32bit,
+       .enable_window_64bit    = gt64260_enable_window_64bit,
+       .disable_window_64bit   = gt64260_disable_window_64bit,
+       .disable_all_windows    = gt64260_disable_all_windows,
+       .chip_specific_init     = gt64260a_chip_specific_init,
+       .window_tab_32bit       = gt64260_32bit_windows,
+       .window_tab_64bit       = gt64260_64bit_windows,
+};
+
+static mv64x60_chip_info_t gt64260b_ci __initdata = {
+       .translate_size         = gt64260_translate_size,
+       .untranslate_size       = gt64260_untranslate_size,
+       .set_pci2mem_window     = gt64260_set_pci2mem_window,
+       .is_enabled_32bit       = gt64260_is_enabled_32bit,
+       .enable_window_32bit    = gt64260_enable_window_32bit,
+       .disable_window_32bit   = gt64260_disable_window_32bit,
+       .enable_window_64bit    = gt64260_enable_window_64bit,
+       .disable_window_64bit   = gt64260_disable_window_64bit,
+       .disable_all_windows    = gt64260_disable_all_windows,
+       .chip_specific_init     = gt64260b_chip_specific_init,
+       .window_tab_32bit       = gt64260_32bit_windows,
+       .window_tab_64bit       = gt64260_64bit_windows,
+};
+
+
+static mv64x60_32bit_window_t mv64360_32bit_windows[] __initdata = {
+       /* CPU->MEM Windows */
+       [MV64x60_CPU2MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 1 },
+       [MV64x60_CPU2MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 2 },
+       [MV64x60_CPU2MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 3 },
+       /* CPU->Device Windows */
+       [MV64x60_CPU2DEV_0_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_0_BASE,
+               .size_reg               = MV64x60_CPU2DEV_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 4 },
+       [MV64x60_CPU2DEV_1_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_1_BASE,
+               .size_reg               = MV64x60_CPU2DEV_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 5 },
+       [MV64x60_CPU2DEV_2_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_2_BASE,
+               .size_reg               = MV64x60_CPU2DEV_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 6 },
+       [MV64x60_CPU2DEV_3_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_3_BASE,
+               .size_reg               = MV64x60_CPU2DEV_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 7 },
+       /* CPU->Boot Window */
+       [MV64x60_CPU2BOOT_WIN] = {
+               .base_reg               = MV64x60_CPU2BOOT_0_BASE,
+               .size_reg               = MV64x60_CPU2BOOT_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 8 },
+       /* CPU->PCI 0 Windows */
+       [MV64x60_CPU2PCI0_IO_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_IO_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_IO_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 9 },
+       [MV64x60_CPU2PCI0_MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 10 },
+       [MV64x60_CPU2PCI0_MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 11 },
+       [MV64x60_CPU2PCI0_MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 12 },
+       [MV64x60_CPU2PCI0_MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 13 },
+       /* CPU->PCI 1 Windows */
+       [MV64x60_CPU2PCI1_IO_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_IO_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_IO_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 14 },
+       [MV64x60_CPU2PCI1_MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 15 },
+       [MV64x60_CPU2PCI1_MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 16 },
+       [MV64x60_CPU2PCI1_MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 17 },
+       [MV64x60_CPU2PCI1_MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 18 },
+       /* CPU->SRAM Window */
+       [MV64x60_CPU2SRAM_WIN] = {
+               .base_reg               = MV64360_CPU2SRAM_BASE,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 19 },
+       /* CPU->PCI 0 Remap I/O Window */
+       [MV64x60_CPU2PCI0_IO_REMAP_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_IO_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 Remap I/O Window */
+       [MV64x60_CPU2PCI1_IO_REMAP_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_IO_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU Memory Protection Windows */
+       [MV64x60_CPU_PROT_0_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_0,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_0,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0x80000000 | 31 },
+       [MV64x60_CPU_PROT_1_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_1,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_1,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0x80000000 | 31 },
+       [MV64x60_CPU_PROT_2_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_2,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_2,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0x80000000 | 31 },
+       [MV64x60_CPU_PROT_3_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_3,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_3,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0x80000000 | 31 },
+       /* CPU Snoop Windows -- don't exist on 64360 */
+       /* PCI 0->System Memory Remap Windows */
+       [MV64x60_PCI02MEM_REMAP_0_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_0_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_1_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_2_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_3_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       /* PCI 1->System Memory Remap Windows */
+       [MV64x60_PCI12MEM_REMAP_0_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_0_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_1_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_2_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_3_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+};
+
+static mv64x60_64bit_window_t mv64360_64bit_windows[MV64x60_64BIT_WIN_COUNT]
+                                                               __initdata = {
+       /* CPU->PCI 0 MEM Remap Windows */
+       [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_0_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_0_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_1_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_1_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_2_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_2_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_3_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_3_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 MEM Remap Windows */
+       [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_0_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_0_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_1_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_1_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_2_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_2_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_3_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_3_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 0->MEM Access Control Windows */
+       [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_0_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_0_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_0_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0x80000000 | 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_1_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_1_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_1_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0x80000000 | 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_2_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_2_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_2_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0x80000000 | 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_3_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_3_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_3_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0x80000000 | 0 },
+       /* PCI 1->MEM Access Control Windows */
+       [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_0_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_0_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_0_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0x80000000 | 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_1_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_1_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_1_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0x80000000 | 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_2_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_2_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_2_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0x80000000 | 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_3_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_3_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_3_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0x80000000 | 0 },
+       /* PCI 0->MEM Snoop Windows -- don't exist on 64360 */
+       /* PCI 1->MEM Snoop Windows -- don't exist on 64360 */
+};
+
+static mv64x60_chip_info_t mv64360_ci __initdata = {
+       .translate_size         = mv64360_translate_size,
+       .untranslate_size       = mv64360_untranslate_size,
+       .set_pci2mem_window     = mv64360_set_pci2mem_window,
+       .is_enabled_32bit       = mv64360_is_enabled_32bit,
+       .enable_window_32bit    = mv64360_enable_window_32bit,
+       .disable_window_32bit   = mv64360_disable_window_32bit,
+       .enable_window_64bit    = mv64360_enable_window_64bit,
+       .disable_window_64bit   = mv64360_disable_window_64bit,
+       .disable_all_windows    = mv64360_disable_all_windows,
+       .chip_specific_init     = mv64360_chip_specific_init,
+       .window_tab_32bit       = mv64360_32bit_windows,
+       .window_tab_64bit       = mv64360_64bit_windows,
+};
+
+static mv64x60_chip_info_t mv64460_ci __initdata = {
+       .translate_size         = mv64360_translate_size,
+       .untranslate_size       = mv64360_untranslate_size,
+       .set_pci2mem_window     = mv64360_set_pci2mem_window,
+       .is_enabled_32bit       = mv64360_is_enabled_32bit,
+       .enable_window_32bit    = mv64360_enable_window_32bit,
+       .disable_window_32bit   = mv64360_disable_window_32bit,
+       .enable_window_64bit    = mv64360_enable_window_64bit,
+       .disable_window_64bit   = mv64360_disable_window_64bit,
+       .disable_all_windows    = mv64360_disable_all_windows,
+       .chip_specific_init     = mv64460_chip_specific_init,
+       .window_tab_32bit       = mv64360_32bit_windows,
+       .window_tab_64bit       = mv64360_64bit_windows,
+};
+
+
+/*
+ *****************************************************************************
+ *
+ *     Bridge Initialization Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_init()
+ *
+ * Initialze the bridge based on setting passed in via 'si'.  The bridge
+ * handle, 'bh', will be set so that it can be used to make subsequent
+ * calls to routines in this file.
+ */
+int __init
+mv64x60_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       u32     mem_windows[MV64x60_CPU2MEM_WINDOWS][2];
+       int     rc = 0;
+
+       if (ppc_md.progress)
+               ppc_md.progress("mv64x60_init: Enter", 0x0);
+
+       mv64x60_early_init(bh, si);
+       mv64x60_alloc_hoses(bh, si); /* Allocate pci hose structures */
+       if (mv64x60_get_type(bh))
+               return -1;
+
+       if (mv64x60_setup_for_chip(bh) != 0) {
+               iounmap((void *)bh->v_base);
+
+               if (ppc_md.progress)
+                       ppc_md.progress("mv64x60_init: Exit--error", 0x0);
+               return -1;
+       }
+
+       bh->ci->disable_all_windows(bh, si); /* Disable windows except mem ctlr */
+       mv64x60_config_cpu2pci_windows(bh, si); /* Init CPU->PCI windows */
+       mv64x60_get_mem_windows(bh, mem_windows); /* Read mem ctlr regs */
+       mv64x60_config_cpu2mem_windows(bh, si, mem_windows); /* CPU->MEM setup*/
+       mv64x60_config_pci2mem_windows(bh, si, mem_windows); /* PCI->Sys MEM */
+       mv64x60_init_hoses(bh, si); /* Init hose structs & PCI params */
+       bh->ci->chip_specific_init(bh, si);
+       mv64x60_enumerate_buses(bh, si); /* Enumerate PCI buses */
+       ocp_for_each_device(mv64x60_fixup_ocp, (void *)bh);
+
+       if (ppc_md.progress)
+               ppc_md.progress("mv64x60_init: Exit", 0x0);
+
+       return rc;
+} /* mv64x60_init() */
+
+/*
+ *****************************************************************************
+ *
+ *     Pre-Bridge-Init Routines (Externally Visible)
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_get_mem_size()
+ *
+ * Calculate the amount of memory that the memory controller is set up for.
+ * This should only be used by board-specific code if there is no other
+ * way to determine the amount of memory in the system.
+ */
+u32 __init
+mv64x60_get_mem_size(u32 bridge_base, u32 chip_type)
+{
+       mv64x60_handle_t        bh;
+       u32                     mem_windows[MV64x60_CPU2MEM_WINDOWS][2];
+
+       memset(&bh, 0, sizeof(bh));
+
+       bh.type = chip_type;
+       bh.p_base = bridge_base;
+       bh.v_base = bridge_base;
+
+       (void)mv64x60_setup_for_chip(&bh);
+       mv64x60_get_mem_windows(&bh, mem_windows);
+       return mv64x60_calc_mem_size(&bh, mem_windows);
+}
+
+/*
+ *****************************************************************************
+ *
+ *     Window Config Routines (Externally Visible)
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_get_32bit_window()
+ *
+ * Determine the base address and size of a 32-bit window on the bridge.
+ */
+void __init
+mv64x60_get_32bit_window(mv64x60_handle_t *bh, u32 window, u32 *base, u32 *size)
+{
+       u32     val, base_reg, size_reg, base_bits, size_bits;
+       u32     (*get_from_field)(u32 val, u32 num_bits);
+
+       base_reg  = bh->ci->window_tab_32bit[window].base_reg;
+
+       if (base_reg != 0) {
+               size_reg  = bh->ci->window_tab_32bit[window].size_reg;
+               base_bits = bh->ci->window_tab_32bit[window].base_bits;
+               size_bits = bh->ci->window_tab_32bit[window].size_bits;
+               get_from_field= bh->ci->window_tab_32bit[window].get_from_field;
+
+               val = mv64x60_read(bh, base_reg);
+               *base = get_from_field(val, base_bits);
+
+               if (size_reg != 0) {
+                       val = mv64x60_read(bh, size_reg);
+                       val = get_from_field(val, size_bits);
+                       *size = bh->ci->untranslate_size(*base, val, size_bits);
+               }
+               else {
+                       *size = 0;
+               }
+       }
+       else {
+               *base = 0;
+               *size = 0;
+       }
+
+       DBG("get 32bit window: %d, base: 0x%x, size: 0x%x\n",
+               window, *base, *size);
+
+       return;
+}
+
+/*
+ * mv64x60_set_32bit_window()
+ *
+ * Set the base address and size of a 32-bit window on the bridge.
+ */
+void __init
+mv64x60_set_32bit_window(mv64x60_handle_t *bh, u32 window, u32 base, u32 size,
+                                                               u32 other_bits)
+{
+       u32     val, base_reg, size_reg, base_bits, size_bits;
+       u32     (*map_to_field)(u32 val, u32 num_bits);
+
+       DBG("set 32bit window: %d, base: 0x%x, size: 0x%x, other: 0x%x\n",
+               window, base, size, other_bits);
+
+       base_reg  = bh->ci->window_tab_32bit[window].base_reg;
+
+       if (base_reg != 0) {
+               size_reg  = bh->ci->window_tab_32bit[window].size_reg;
+               base_bits = bh->ci->window_tab_32bit[window].base_bits;
+               size_bits = bh->ci->window_tab_32bit[window].size_bits;
+               map_to_field = bh->ci->window_tab_32bit[window].map_to_field;
+
+               val = map_to_field(base, base_bits) | other_bits;
+               mv64x60_write(bh, base_reg, val);
+
+               if (size_reg != 0) {
+                       val = bh->ci->translate_size(base, size, size_bits);
+                       val = map_to_field(val, size_bits);
+                       mv64x60_write(bh, size_reg, val);
+               }
+               (void)mv64x60_read(bh, base_reg); /* Flush FIFO */
+       }
+
+       return;
+}
+
+/*
+ * mv64x60_get_64bit_window()
+ *
+ * Determine the base address and size of a 64-bit window on the bridge.
+ */
+void __init
+mv64x60_get_64bit_window(mv64x60_handle_t *bh, u32 window, u32 *base_hi,
+                                                       u32 *base_lo, u32 *size)
+{
+       u32     val, base_lo_reg, size_reg, base_lo_bits, size_bits;
+       u32     (*get_from_field)(u32 val, u32 num_bits);
+
+       base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg;
+
+       if (base_lo_reg != 0) {
+               size_reg = bh->ci->window_tab_64bit[window].size_reg;
+               base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits;
+               size_bits = bh->ci->window_tab_64bit[window].size_bits;
+               get_from_field= bh->ci->window_tab_64bit[window].get_from_field;
+
+               *base_hi = mv64x60_read(bh, 
+                       bh->ci->window_tab_64bit[window].base_hi_reg);
+
+               val = mv64x60_read(bh, base_lo_reg);
+               *base_lo = get_from_field(val, base_lo_bits);
+
+               if (size_reg != 0) {
+                       val = mv64x60_read(bh, size_reg);
+                       val = get_from_field(val, size_bits);
+                       *size = bh->ci->untranslate_size(*base_lo, val,
+                                                               size_bits);
+               }
+               else {
+                       *size = 0;
+               }
+       }
+       else {
+               *base_hi = 0;
+               *base_lo = 0;
+               *size = 0;
+       }
+
+       DBG("get 64bit window: %d, base hi: 0x%x, base lo: 0x%x, size: 0x%x\n",
+               window, *base_hi, *base_lo, *size);
+
+       return;
+}
+
+/*
+ * mv64x60_set_64bit_window()
+ *
+ * Set the base address and size of a 64-bit window on the bridge.
+ */
+void __init
+mv64x60_set_64bit_window(mv64x60_handle_t *bh, u32 window,
+                       u32 base_hi, u32 base_lo, u32 size, u32 other_bits)
+{
+       u32     val, base_lo_reg, size_reg, base_lo_bits, size_bits;
+       u32     (*map_to_field)(u32 val, u32 num_bits);
+
+       DBG("set 64bit window: %d, base hi: 0x%x, base lo: 0x%x, " \
+               "size: 0x%x, other: 0x%x\n",
+               window, base_hi, base_lo, size, other_bits);
+
+       base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg;
+
+       if (base_lo_reg != 0) {
+               size_reg = bh->ci->window_tab_64bit[window].size_reg;
+               base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits;
+               size_bits = bh->ci->window_tab_64bit[window].size_bits;
+               map_to_field = bh->ci->window_tab_64bit[window].map_to_field;
+
+               mv64x60_write(bh, bh->ci->window_tab_64bit[window].base_hi_reg,
+                       base_hi);
+
+               val = map_to_field(base_lo, base_lo_bits) | other_bits;
+               mv64x60_write(bh, base_lo_reg, val);
+
+               if (size_reg != 0) {
+                       val = bh->ci->translate_size(base_lo, size, size_bits);
+                       val = map_to_field(val, size_bits);
+                       mv64x60_write(bh, size_reg, val);
+               }
+
+               (void)mv64x60_read(bh, base_lo_reg); /* Flush FIFO */
+       }
+
+       return;
+}
+
+/*
+ * mv64x60_mask()
+ *
+ * Take the high-order 'num_bits' of 'val' & mask off low bits.
+ */
+static u32 __init
+mv64x60_mask(u32 val, u32 num_bits)
+{
+       DBG("mask val: 0x%x, num_bits: %d == 0x%x\n", val,
+               num_bits, val & (0xffffffff << (32 - num_bits)));
+
+       return val & (0xffffffff << (32 - num_bits));
+}
+
+/*
+ * mv64x60_mask_shift_left()
+ *
+ * Take the low-order 'num_bits' of 'val', shift left to align at bit 31 (MSB).
+ */
+static u32 __init
+mv64x60_shift_left(u32 val, u32 num_bits)
+{
+       DBG("shift left val: 0x%x, num_bits: %d == 0x%x\n", val,
+               num_bits, val << (32 - num_bits));
+
+       return val << (32 - num_bits);
+}
+
+/*
+ * mv64x60_shift_right()
+ *
+ * Take the high-order 'num_bits' of 'val', shift right to align at bit 0 (LSB).
+ */
+static u32 __init
+mv64x60_shift_right(u32 val, u32 num_bits)
+{
+       DBG("shift right val: 0x%x, num_bits: %d == 0x%x\n", val, num_bits,
+               val >> (32 - num_bits));
+
+       return val >> (32 - num_bits);
+}
+
+/*
+ *****************************************************************************
+ *
+ *     Early Init Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_early_init()
+ *
+ * Do some bridge work that must take place before we start messing with
+ * the bridge for real.
+ */
+static void __init
+mv64x60_early_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       memset(bh, 0, sizeof(*bh));
+
+       bh->p_base = si->phys_reg_base;
+       bh->v_base = (u32)ioremap(bh->p_base, MV64x60_INTERNAL_SPACE_SIZE);
+       bh->base_irq = si->base_irq;
+
+       /* Bit 12 MUST be 0; set bit 27--don't auto-update cpu remap regs */
+       mv64x60_clr_bits(bh, MV64x60_CPU_CONFIG, (1<<12));
+       mv64x60_set_bits(bh, MV64x60_CPU_CONFIG, (1<<27));
+
+       /*
+        * Turn off timer/counters.  Not turning off watchdog timer because
+        * can't read its reg on the 64260A so don't know if we'll be enabling
+        * or disabling.
+        */
+       mv64x60_clr_bits(bh, MV64x60_TIMR_CNTR_0_3_CNTL,
+                       ((1<<0) | (1<<8) | (1<<16) | (1<<24)));
+
+#ifdef CONFIG_GT64260
+       mv64x60_clr_bits(bh, GT64260_TIMR_CNTR_4_7_CNTL,
+                       ((1<<0) | (1<<8) | (1<<16) | (1<<24)));
+#endif
+
+#if 0
+XXXX Put in PCI_x_RETRY adjustment XXXX
+#endif
+
+       return;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     Chip Identification Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_get_type()
+ *
+ * Determine the type of bridge chip we have.
+ */
+static int __init mv64x60_get_type(struct mv64x60_handle *bh)
+{
+       struct pci_controller *hose = bh->hose_a;
+       int pcidev;
+       int devfn;
+       u16 val;
+       u8 save_exclude;
+
+       pcidev = (mv64x60_read(bh, MV64x60_PCI0_P2P_CONFIG) >> 24) & 0xf;
+       devfn = PCI_DEVFN(pcidev, 0); 
+
+       save_exclude = mv64x60_pci_exclude_bridge;
+       mv64x60_pci_exclude_bridge = FALSE;
+
+       /* Sanity check of bridge's Vendor ID */
+       early_read_config_word(hose, 0, devfn, PCI_VENDOR_ID, &val);
+
+       if (val != PCI_VENDOR_ID_MARVELL)
+               return -1;
+
+       /* Figure out the type of Marvell bridge it is */
+       early_read_config_word(hose, 0, devfn, PCI_DEVICE_ID, &val);
+
+       switch (val) {
+       case PCI_DEVICE_ID_MARVELL_GT64260:
+               early_read_config_word(hose, 0, devfn,
+                                      PCI_CLASS_REVISION, &val);
+
+               switch (val & 0xff) {
+               case GT64260_REV_A:
+                       bh->type = MV64x60_TYPE_GT64260A;
+                       break;
+               case GT64260_REV_B:
+                       bh->type = MV64x60_TYPE_GT64260B;
+                       break;
+               }
+               break;
+
+       case PCI_DEVICE_ID_MARVELL_MV64360:
+               /* Marvell won't tell me how to distinguish a 64361 & 64362 */
+               bh->type = MV64x60_TYPE_MV64360;
+               break;
+
+       case PCI_DEVICE_ID_MARVELL_MV64460:
+               bh->type = MV64x60_TYPE_MV64460;
+               break;
+
+       default:
+               printk(KERN_CRIT "Unknown Marvell bridge type %04x\n", val);
+               return -1;
+       }
+
+       mv64x60_pci_exclude_bridge = save_exclude;
+       return 0;
+}
+
+/*
+ * mv64x60_setup_for_chip()
+ *
+ * Set 'bh' to use the proper set of routine for the bridge chip that we have.
+ */
+static int __init
+mv64x60_setup_for_chip(mv64x60_handle_t *bh)
+{
+       int     rc = 0;
+
+       /* Set up chip-specific info based on the chip/bridge type */
+       switch(bh->type) {
+               case MV64x60_TYPE_GT64260A:
+                       bh->ci = &gt64260a_ci;
+                       break;
+
+               case MV64x60_TYPE_GT64260B:
+                       bh->ci = &gt64260b_ci;
+                       break;
+
+               case MV64x60_TYPE_MV64360:
+                       bh->ci = &mv64360_ci;
+                       break;
+
+#if 0 /* Marvell won't tell me how to distinguish--MAG */
+               case MV64x60_TYPE_MV64361:
+               case MV64x60_TYPE_MV64362:
+#endif
+               case MV64x60_TYPE_MV64460:
+                       bh->ci = &mv64460_ci;
+                       break;
+
+               case MV64x60_TYPE_INVALID:
+               default:
+                       if (ppc_md.progress)
+                               ppc_md.progress("mv64x60: Unsupported bridge",
+                                                                       0x0);
+                       printk("mv64x60: Unsupported bridge\n");
+                       rc = -1;
+       }
+
+       return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     System Memory Window Related Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_get_mem_windows()
+ *
+ * Get the values in the memory controller & return in the 'mem_windows' array.
+ */
+static void __init
+mv64x60_get_mem_windows(mv64x60_handle_t *bh,
+       u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+       u32     i;
+       u32     windows[] = { MV64x60_CPU2MEM_0_WIN, MV64x60_CPU2MEM_1_WIN,
+                               MV64x60_CPU2MEM_2_WIN, MV64x60_CPU2MEM_3_WIN };
+
+       for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) {
+               if (bh->ci->is_enabled_32bit(bh, i)) {
+                       mv64x60_get_32bit_window(bh, windows[i],
+                               &mem_windows[i][0], &mem_windows[i][1]);
+               }
+               else {
+                       mem_windows[i][0] = 0;
+                       mem_windows[i][1] = 0;
+               }
+       }
+
+       return;
+}
+
+/*
+ * mv64x60_calc_mem_size()
+ *
+ * Using the memory controller register values in 'mem_windows', determine
+ * how much memory it is set up for.
+ */
+static u32 __init
+mv64x60_calc_mem_size(mv64x60_handle_t *bh,
+       u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+       u32     i, total = 0;
+
+       for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) {
+               total += mem_windows[i][1];
+       }
+
+       return total;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     CPU->System MEM Config Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_config_cpu2mem_windows()
+ *
+ * Configure CPU->Memory windows on the bridge.
+ */
+static void __init
+mv64x60_config_cpu2mem_windows(mv64x60_handle_t *bh, mv64x60_setup_info_t *si,
+       u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+       u32     i;
+       u32     prot_windows[] = {
+                       MV64x60_CPU_PROT_0_WIN, MV64x60_CPU_PROT_1_WIN,
+                       MV64x60_CPU_PROT_2_WIN, MV64x60_CPU_PROT_3_WIN };
+       u32     cpu_snoop_windows[] = {
+                       MV64x60_CPU_SNOOP_0_WIN, MV64x60_CPU_SNOOP_1_WIN,
+                       MV64x60_CPU_SNOOP_2_WIN, MV64x60_CPU_SNOOP_3_WIN };
+
+       /* Set CPU protection & snoop windows */
+       for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) {
+               if (bh->ci->is_enabled_32bit(bh, i)) {
+                       mv64x60_set_32bit_window(bh, prot_windows[i],
+                               mem_windows[i][0], mem_windows[i][1],
+                               si->cpu_prot_options[i]);
+                       bh->ci->enable_window_32bit(bh, prot_windows[i]);
+
+                       if (bh->ci->window_tab_32bit[cpu_snoop_windows[i]].
+                                                               base_reg != 0) {
+                               mv64x60_set_32bit_window(bh,
+                                       cpu_snoop_windows[i], mem_windows[i][0],
+                                       mem_windows[i][1],
+                                       si->cpu_snoop_options[i]);
+                               bh->ci->enable_window_32bit(bh,
+                                       cpu_snoop_windows[i]);
+                       }
+
+               }
+       }
+
+       return;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     CPU->PCI Config Routines
+ *
+ *****************************************************************************
+ */
+
+/*
+ * mv64x60_config_cpu2pci_windows()
+ *
+ * Configure the CPU->PCI windows on the bridge.
+ */
+static void __init
+mv64x60_config_cpu2pci_windows(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("mv64x60_config_bridge: Enter", 0x0);
+
+       /*
+        * Set up various parts of the bridge including CPU->PCI windows.
+        * Depending on the board, there may be only one hose that needs to
+        * be set up.
+        */
+       if (si->pci_0.enable_bus) {
+               u32     win_tab[] = { MV64x60_CPU2PCI0_IO_WIN,
+                               MV64x60_CPU2PCI0_MEM_0_WIN,
+                               MV64x60_CPU2PCI0_MEM_1_WIN,
+                               MV64x60_CPU2PCI0_MEM_2_WIN };
+               u32     remap_tab[] = { MV64x60_CPU2PCI0_IO_REMAP_WIN,
+                               MV64x60_CPU2PCI0_MEM_0_REMAP_WIN,
+                               MV64x60_CPU2PCI0_MEM_1_REMAP_WIN,
+                               MV64x60_CPU2PCI0_MEM_2_REMAP_WIN };
+
+               mv64x60_set_cpu2pci_window(bh, &si->pci_0, win_tab, remap_tab);
+       }
+
+       if (si->pci_1.enable_bus) {
+               u32     win_tab[] = { MV64x60_CPU2PCI1_IO_WIN,
+                               MV64x60_CPU2PCI1_MEM_0_WIN,
+                               MV64x60_CPU2PCI1_MEM_1_WIN,
+                               MV64x60_CPU2PCI1_MEM_2_WIN };
+               u32     remap_tab[] = { MV64x60_CPU2PCI1_IO_REMAP_WIN,
+                               MV64x60_CPU2PCI1_MEM_0_REMAP_WIN,
+                               MV64x60_CPU2PCI1_MEM_1_REMAP_WIN,
+                               MV64x60_CPU2PCI1_MEM_2_REMAP_WIN };
+
+               mv64x60_set_cpu2pci_window(bh, &si->pci_1, win_tab, remap_tab);
+       }
+
+       return;
+} /* mv64x60_config_bridge() */
+
+/*
+ * mv64x60_set_cpu2pci_window()
+ *
+ * Configure the CPU->PCI windows for one of the PCI buses.
+ */
+static void __init
+mv64x60_set_cpu2pci_window(mv64x60_handle_t *bh, mv64x60_pci_info_t *pi,
+                                               u32 *win_tab, u32 *remap_tab)
+{
+       int     i;
+
+       if (pi->pci_io.size > 0) {
+               mv64x60_set_32bit_window(bh, win_tab[0], pi->pci_io.cpu_base,
+                                       pi->pci_io.size, pi->pci_io.swap);
+               mv64x60_set_32bit_window(bh, remap_tab[0],
+                                       pi->pci_io.pci_base_lo, 0, 0);
+               bh->ci->enable_window_32bit(bh, win_tab[0]);
+       }
+       else { /* Actually, the window should already be disabled */
+               bh->ci->disable_window_32bit(bh, win_tab[0]);
+       }
+
+       for (i=0; i<3; i++) {
+               if (pi->pci_mem[i].size > 0) {
+                       mv64x60_set_32bit_window(bh, win_tab[i+1],
+                               pi->pci_mem[i].cpu_base, pi->pci_mem[i].size,
+                               pi->pci_mem[i].swap);
+                       mv64x60_set_64bit_window(bh, remap_tab[i+1],
+                               pi->pci_mem[i].pci_base_hi,
+                               pi->pci_mem[i].pci_base_lo, 0, 0);
+                       bh->ci->enable_window_32bit(bh, win_tab[i+1]);
+               }
+               else { /* Actually, the window should already be disabled */
+                       bh->ci->disable_window_32bit(bh, win_tab[i+1]);
+               }
+       }
+
+       return;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     PCI->System MEM Config Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_config_pci2mem_windows()
+ *
+ * Configure the PCI->Memory windows on the bridge.
+ */
+static void __init
+mv64x60_config_pci2mem_windows(mv64x60_handle_t *bh, mv64x60_setup_info_t *si,
+       u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+       u32     i;
+       u32     pci_0_acc_windows[] = {
+                       MV64x60_PCI02MEM_ACC_CNTL_0_WIN,
+                       MV64x60_PCI02MEM_ACC_CNTL_1_WIN,
+                       MV64x60_PCI02MEM_ACC_CNTL_2_WIN,
+                       MV64x60_PCI02MEM_ACC_CNTL_3_WIN };
+       u32     pci_1_acc_windows[] = {
+                       MV64x60_PCI12MEM_ACC_CNTL_0_WIN,
+                       MV64x60_PCI12MEM_ACC_CNTL_1_WIN,
+                       MV64x60_PCI12MEM_ACC_CNTL_2_WIN,
+                       MV64x60_PCI12MEM_ACC_CNTL_3_WIN };
+       u32     pci_0_snoop_windows[] = {
+                       MV64x60_PCI02MEM_SNOOP_0_WIN,
+                       MV64x60_PCI02MEM_SNOOP_1_WIN,
+                       MV64x60_PCI02MEM_SNOOP_2_WIN,
+                       MV64x60_PCI02MEM_SNOOP_3_WIN };
+       u32     pci_1_snoop_windows[] = {
+                       MV64x60_PCI12MEM_SNOOP_0_WIN,
+                       MV64x60_PCI12MEM_SNOOP_1_WIN,
+                       MV64x60_PCI12MEM_SNOOP_2_WIN,
+                       MV64x60_PCI12MEM_SNOOP_3_WIN };
+       u32     pci_0_size[] = {
+                       MV64x60_PCI0_MEM_0_SIZE, MV64x60_PCI0_MEM_1_SIZE,
+                       MV64x60_PCI0_MEM_2_SIZE, MV64x60_PCI0_MEM_3_SIZE };
+       u32     pci_1_size[] = {
+                       MV64x60_PCI1_MEM_0_SIZE, MV64x60_PCI1_MEM_1_SIZE,
+                       MV64x60_PCI1_MEM_2_SIZE, MV64x60_PCI1_MEM_3_SIZE };
+
+       /* Clear bit 0 of PCI addr decode control so PCI->CPU remap 1:1 */
+       mv64x60_clr_bits(bh, MV64x60_PCI0_PCI_DECODE_CNTL, 0x00000001);
+       mv64x60_clr_bits(bh, MV64x60_PCI1_PCI_DECODE_CNTL, 0x00000001);
+
+       /*
+        * Set the access control, snoop, BAR size, and window base addresses.
+        * PCI->MEM windows base addresses will match exactly what the
+        * CPU->MEM windows are.
+        */
+       for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) {
+               if (bh->ci->is_enabled_32bit(bh, i)) {
+                       if (si->pci_0.enable_bus) {
+                               mv64x60_set_64bit_window(bh,
+                                       pci_0_acc_windows[i], 0,
+                                       mem_windows[i][0], mem_windows[i][1],
+                                       si->pci_0.acc_cntl_options[i]);
+                               bh->ci->enable_window_64bit(bh,
+                                       pci_0_acc_windows[i]);
+
+                               if (bh->ci->window_tab_64bit[
+                                       pci_0_snoop_windows[i]].base_lo_reg
+                                                                       != 0) {
+                                       mv64x60_set_64bit_window(bh,
+                                               pci_0_snoop_windows[i], 0,
+                                               mem_windows[i][0],
+                                               mem_windows[i][1],
+                                               si->pci_0.snoop_options[i]);
+                                       bh->ci->enable_window_64bit(bh,
+                                               pci_0_snoop_windows[i]);
+                               }
+
+                               bh->ci->set_pci2mem_window(bh->hose_a, i,
+                                       mem_windows[i][0]);
+                               mv64x60_write(bh, pci_0_size[i],
+                                       mv64x60_mask(mem_windows[i][1] -1, 20));
+
+                               /* Enable the window */
+                               mv64x60_clr_bits(bh, MV64x60_PCI0_BAR_ENABLE,
+                                                                       1 << i);
+                       }
+                       if (si->pci_1.enable_bus) {
+                               mv64x60_set_64bit_window(bh,
+                                       pci_1_acc_windows[i], 0,
+                                       mem_windows[i][0], mem_windows[i][1],
+                                       si->pci_1.acc_cntl_options[i]);
+                               bh->ci->enable_window_64bit(bh,
+                                       pci_1_acc_windows[i]);
+
+                               if (bh->ci->window_tab_64bit[
+                                       pci_1_snoop_windows[i]].base_lo_reg
+                                                                       != 0) {
+                                       mv64x60_set_64bit_window(bh,
+                                               pci_1_snoop_windows[i], 0,
+                                               mem_windows[i][0],
+                                               mem_windows[i][1],
+                                               si->pci_1.snoop_options[i]);
+                                       bh->ci->enable_window_64bit(bh,
+                                               pci_1_snoop_windows[i]);
+                               }
+
+                               bh->ci->set_pci2mem_window(bh->hose_b, i,
+                                       mem_windows[i][0]);
+                               mv64x60_write(bh, pci_1_size[i],
+                                       mv64x60_mask(mem_windows[i][1] -1, 20));
+
+                               /* Enable the window */
+                               mv64x60_clr_bits(bh, MV64x60_PCI1_BAR_ENABLE,
+                                                                       1 << i);
+                       }
+               }
+       }
+
+       return;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     Hose & Resource Alloc/Init Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_alloc_hoses()
+ *
+ * Allocate the PCI hose structures for the bridge's PCI buses.
+ */
+static void __init
+mv64x60_alloc_hoses(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       /*
+        * Alloc first hose struct even when its not to be configured b/c the
+        * chip identification routines need to use it.
+        */
+       bh->hose_a = pcibios_alloc_controller();
+       setup_indirect_pci(bh->hose_a,
+               bh->p_base + MV64x60_PCI0_CONFIG_ADDR,
+               bh->p_base + MV64x60_PCI0_CONFIG_DATA);
+
+       if (si->pci_1.enable_bus) {
+               bh->hose_b = pcibios_alloc_controller();
+               setup_indirect_pci(bh->hose_b,
+                       bh->p_base + MV64x60_PCI1_CONFIG_ADDR,
+                       bh->p_base + MV64x60_PCI1_CONFIG_DATA);
+       }
+
+       return;
+}
+
+/*
+ * mv64x60_init_hoses()
+ *
+ * Initialize the PCI hose structures for the bridge's PCI hoses.
+ */
+static void __init
+mv64x60_init_hoses(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       if (si->pci_1.enable_bus) {
+               bh->io_base_b = (u32)ioremap(si->pci_1.pci_io.cpu_base,
+                                                       si->pci_1.pci_io.size);
+               isa_io_base = bh->io_base_b;
+       }
+
+       if (si->pci_0.enable_bus) {
+               bh->io_base_a = (u32)ioremap(si->pci_0.pci_io.cpu_base,
+                                                       si->pci_0.pci_io.size);
+               isa_io_base = bh->io_base_a;
+
+               mv64x60_init_resources(bh->hose_a, &si->pci_0, bh->io_base_a);
+               mv64x60_set_pci_params(bh->hose_a, &si->pci_0);
+       }
+
+       /* Must do here so proper isa_io_base is used in calculations */
+       if (si->pci_1.enable_bus) {
+               mv64x60_init_resources(bh->hose_b, &si->pci_1, bh->io_base_b);
+               mv64x60_set_pci_params(bh->hose_b, &si->pci_1);
+       }
+
+       return;
+}
+
+/*
+ * mv64x60_init_resources()
+ *
+ * Calculate the offsets, etc. for the hose structures to reflect all of
+ * the address remapping that happens as you go from CPU->PCI and PCI->MEM.
+ */
+static void __init
+mv64x60_init_resources(struct pci_controller *hose, mv64x60_pci_info_t *pi,
+                               u32 io_base)
+{
+       int             i;
+       /* 2 hoses; 4 resources/hose; sting <= 64 bytes; not work if > 1 chip */
+       static char     s[2][4][64];
+
+       if (pi->pci_io.size != 0) {
+               sprintf(s[hose->index][0], "PCI hose %d I/O Space",
+                       hose->index);
+               pci_init_resource(&hose->io_resource, io_base - isa_io_base,
+                       io_base - isa_io_base + pi->pci_io.size - 1,
+                       IORESOURCE_IO, s[hose->index][0]);
+               hose->io_space.start = pi->pci_io.pci_base_lo;
+               hose->io_space.end = pi->pci_io.pci_base_lo + pi->pci_io.size-1;
+               hose->io_base_virt = (void *)isa_io_base;
+       }
+
+       for (i=0; i<3; i++) {
+               if (pi->pci_mem[i].size != 0) {
+                       sprintf(s[hose->index][i+1], "PCI hose %d MEM Space %d",
+                               hose->index, i);
+                       pci_init_resource(&hose->mem_resources[i],
+                               pi->pci_mem[i].cpu_base,
+                               pi->pci_mem[i].cpu_base + pi->pci_mem[i].size-1,
+                               IORESOURCE_MEM, s[hose->index][i+1]);
+               }
+       }
+       
+       hose->mem_space.end = pi->pci_mem[0].pci_base_lo +
+                                               pi->pci_mem[0].size - 1;
+       hose->pci_mem_offset = pi->pci_mem[0].cpu_base -
+                                               pi->pci_mem[0].pci_base_lo;
+
+       return;
+} /* mv64x60_init_resources() */
+
+/*
+ * mv64x60_set_pci_params()
+ *
+ * Configure a hose's PCI config space parameters.
+ */
+static void __init
+mv64x60_set_pci_params(struct pci_controller *hose, mv64x60_pci_info_t *pi)
+{
+       u32     devfn;
+       u16     u16_val;
+       u8      save_exclude;
+
+       devfn = PCI_DEVFN(0,0);
+
+       save_exclude = mv64x60_pci_exclude_bridge;
+       mv64x60_pci_exclude_bridge = FALSE;
+
+       /* Set class code to indicate host bridge */
+       u16_val = PCI_CLASS_BRIDGE_HOST; /* 0x0600 (host bridge) */
+       early_write_config_word(hose, 0, devfn, PCI_CLASS_DEVICE, u16_val);
+
+       /* Enable 64260 to be PCI master & respond to PCI MEM cycles */
+       early_read_config_word(hose, 0, devfn, PCI_COMMAND, &u16_val);
+       u16_val &= ~(PCI_COMMAND_IO | PCI_COMMAND_INVALIDATE |
+               PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK);
+       u16_val |= pi->pci_cmd_bits | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+       early_write_config_word(hose, 0, devfn, PCI_COMMAND, u16_val);
+
+       /* Set latency timer, cache line size, clear BIST */
+       u16_val = (pi->latency_timer << 8) | (L1_CACHE_LINE_SIZE >> 2);
+       early_write_config_word(hose, 0, devfn, PCI_CACHE_LINE_SIZE, u16_val);
+
+       mv64x60_pci_exclude_bridge = save_exclude;
+       return;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     PCI Related Routine
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_enumerate_buses()
+ *
+ * If requested, enumerate the PCI buses and set the appropriate
+ * info in the hose structures.
+ */
+static void __init
+mv64x60_enumerate_buses(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       u32     val;
+
+       pci_dram_offset = 0; /* System mem at same addr on PCI & cpu bus */
+
+       ppc_md.pci_exclude_device = mv64x60_pci_exclude_device;
+       ppc_md.pci_swizzle = common_swizzle;
+       ppc_md.pci_map_irq = si->map_irq;
+
+       /* Now that the bridge is set up, its safe to scan the PCI buses */
+       if (si->pci_0.enable_bus) {
+               if (si->pci_0.enumerate_bus) {
+                       /* Set bus number for PCI 0 to 0 */
+                       val = mv64x60_read(bh, MV64x60_PCI0_P2P_CONFIG);
+                       val &= 0xe0000000;
+                       val |= 0x000000ff;
+                       mv64x60_write(bh, MV64x60_PCI0_P2P_CONFIG, val);
+                       /* Flush FIFO*/
+                       (void)mv64x60_read(bh, MV64x60_PCI0_P2P_CONFIG);
+
+#if 0
+XXXX Different if in PCI-X mode (look at mv64360_find_bridges()) XXXX
+#endif
+
+                       bh->hose_a->first_busno = 0;
+                       bh->hose_a->last_busno  = 0xff;
+
+                       bh->hose_a->last_busno = pciauto_bus_scan(bh->hose_a,
+                                               bh->hose_a->first_busno);
+               }
+               else {
+                       /* Assume bridge set up correctly by someone else */
+                       val = mv64x60_read(bh, MV64x60_PCI0_P2P_CONFIG);
+                       bh->hose_a->first_busno = (val & 0x00ff0000) >> 16;
+               }
+       }
+
+       if (si->pci_1.enable_bus) {
+               if (si->pci_1.enumerate_bus) {
+                       if (si->pci_0.enable_bus) {
+                               bh->hose_b->first_busno =
+                                       bh->hose_a->last_busno + 1;
+
+                               /* Set bus number for PCI 1 hose */
+                               val = mv64x60_read(bh, MV64x60_PCI1_P2P_CONFIG);
+                               val &= 0xe0000000;
+                               val |= (bh->hose_b->first_busno << 16) | 0xff;
+                               mv64x60_write(bh, MV64x60_PCI1_P2P_CONFIG, val);
+                               /* Flush FIFO */
+                               (void)mv64x60_read(bh, MV64x60_PCI1_P2P_CONFIG);
+                       }
+                       else {
+                               bh->hose_b->first_busno = 0;
+                       }
+
+                       bh->hose_b->last_busno  = 0xff;
+                       bh->hose_b->last_busno = pciauto_bus_scan(bh->hose_b,
+                                               bh->hose_b->first_busno);
+               }
+               else {
+                       /* Assume bridge set up correctly by someone else */
+                       val = mv64x60_read(bh, MV64x60_PCI1_P2P_CONFIG);
+                       bh->hose_b->first_busno = (val & 0x00ff0000) >> 16;
+                       bh->hose_b->last_busno = 0xff; /* No way to know */
+               }
+       }
+
+       if (si->pci_0.enable_bus && !si->pci_0.enumerate_bus) {
+               if (si->pci_1.enable_bus) {
+                       bh->hose_a->last_busno = bh->hose_b->first_busno - 1;
+               }
+               else {
+                       bh->hose_a->last_busno = 0xff; /* No way to know */
+               }
+       }
+
+       return;
+}
+
+/*
+ * mv64x60_exclude_pci_device()
+ *
+ * This routine is used to make the bridge not appear when the
+ * PCI subsystem is accessing PCI devices (in PCI config space).
+ */
+static int
+mv64x60_pci_exclude_device(u8 bus, u8 devfn)
+{
+       struct pci_controller   *hose;
+
+       hose = pci_bus_to_hose(bus);
+
+       /* Skip slot 0 on both hoses */
+       if ((mv64x60_pci_exclude_bridge == TRUE) &&
+           (PCI_SLOT(devfn) == 0) &&
+           (hose->first_busno == bus)) {
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+       else {
+               return PCIBIOS_SUCCESSFUL;
+       }
+} /* mv64x60_pci_exclude_device() */
+
+/*
+ *****************************************************************************
+ *
+ *     OCP Fixup Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_fixup_ocp()
+ *
+ * Adjust the 'paddr' field in the bridge's OCP entries to reflect where they
+ * really are in the physical address space.
+ */
+static void __init
+mv64x60_fixup_ocp(struct ocp_device *dev, void *arg)
+{
+       mv64x60_handle_t        *bh = (mv64x60_handle_t *)arg;
+
+       if (dev->def->vendor == OCP_VENDOR_MARVELL) {
+               dev->def->paddr += bh->p_base;
+       }
+
+       return;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     GT64260-Specific Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * gt64260_translate_size()
+ *
+ * On the GT64260, the size register is really the "top" address of the window.
+ */
+static u32 __init
+gt64260_translate_size(u32 base, u32 size, u32 num_bits)
+{
+       return base + mv64x60_mask(size - 1, num_bits);
+}
+
+/*
+ * gt64260_untranslate_size()
+ *
+ * Translate the top address of a window into a window size.
+ */
+static u32 __init
+gt64260_untranslate_size(u32 base, u32 size, u32 num_bits)
+{
+       if (size >= base) {
+               size = size - base + (1 << (32 - num_bits));
+       }
+       else {
+               size = 0;
+       }
+
+       return size;
+}
+
+/*
+ * gt64260_set_pci2mem_window()
+ *
+ * The PCI->MEM window registers are actually in PCI config space so need
+ * to set them by setting the correct config space BARs.
+ */
+static void __init
+gt64260_set_pci2mem_window(struct pci_controller *hose, u32 window, u32 base)
+{
+       u32     reg_addrs[] = { 0x10, 0x14, 0x18, 0x1c };
+
+       DBG("set pci->mem window: %d, hose: %d, base: 0x%x\n", window,
+               hose->index, base);
+
+       early_write_config_dword(hose, hose->first_busno,
+                       PCI_DEVFN(0, 0), reg_addrs[window],
+                       mv64x60_mask(base, 20) | 0x8);
+       return;
+}
+
+/*
+ * gt64260_is_enabled_32bit()
+ *
+ * On a GT64260, a window is enabled iff its top address is >= to its base
+ * address.
+ */
+static u32 __init
+gt64260_is_enabled_32bit(mv64x60_handle_t *bh, u32 window)
+{
+       u32     rc = 0;
+
+       if ((gt64260_32bit_windows[window].base_reg != 0) &&
+               (gt64260_32bit_windows[window].size_reg != 0) &&
+               ((mv64x60_read(bh, gt64260_32bit_windows[window].size_reg) &
+                       ((1 << gt64260_32bit_windows[window].size_bits) - 1)) >=
+                (mv64x60_read(bh, gt64260_32bit_windows[window].base_reg) &
+                       ((1 << gt64260_32bit_windows[window].base_bits) - 1)))){
+
+               rc = 1;
+       }
+
+       if (rc) {
+               DBG("32bit window %d is enabled\n", window);
+       }
+       else {
+               DBG("32bit window %d is disabled\n", window);
+       }
+
+       return rc;
+}
+
+/*
+ * gt64260_enable_window_32bit()
+ *
+ * On the GT64260, a window is enabled iff the top address is >= to the base
+ * address of the window.  Since the window has already been configured by
+ * the time this routine is called, we have nothing to do here.
+ */
+static void __init
+gt64260_enable_window_32bit(mv64x60_handle_t *bh, u32 window)
+{
+       DBG("enable 32bit window: %d\n", window);
+       return;
+}
+
+/*
+ * gt64260_disable_window_32bit()
+ *
+ * On a GT64260, you disable a window by setting its top address to be less
+ * than its base address.
+ */
+static void __init
+gt64260_disable_window_32bit(mv64x60_handle_t *bh, u32 window)
+{
+       DBG("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n",
+               window, gt64260_32bit_windows[window].base_reg,
+               gt64260_32bit_windows[window].size_reg);
+
+       if ((gt64260_32bit_windows[window].base_reg != 0) &&
+               (gt64260_32bit_windows[window].size_reg != 0)) {
+
+               /* To disable, make bottom reg higher than top reg */
+               mv64x60_write(bh, gt64260_32bit_windows[window].base_reg,0xfff);
+               mv64x60_write(bh, gt64260_32bit_windows[window].size_reg, 0);
+       }
+
+       return;
+}
+
+/*
+ * gt64260_enable_window_64bit()
+ *
+ * On the GT64260, a window is enabled iff the top address is >= to the base
+ * address of the window.  Since the window has already been configured by
+ * the time this routine is called, we have nothing to do here.
+ */
+static void __init
+gt64260_enable_window_64bit(mv64x60_handle_t *bh, u32 window)
+{
+       DBG("enable 64bit window: %d\n", window);
+       return; /* Enabled when window configured (i.e., when top >= base) */
+}
+
+/*
+ * gt64260_disable_window_64bit()
+ *
+ * On a GT64260, you disable a window by setting its top address to be less
+ * than its base address.
+ */
+static void __init
+gt64260_disable_window_64bit(mv64x60_handle_t *bh, u32 window)
+{
+       DBG("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n",
+               window, gt64260_64bit_windows[window].base_lo_reg,
+               gt64260_64bit_windows[window].size_reg);
+
+       if ((gt64260_64bit_windows[window].base_lo_reg != 0) &&
+               (gt64260_64bit_windows[window].size_reg != 0)) {
+
+               /* To disable, make bottom reg higher than top reg */
+               mv64x60_write(bh, gt64260_64bit_windows[window].base_lo_reg,
+                                                                       0xfff);
+               mv64x60_write(bh, gt64260_64bit_windows[window].base_hi_reg, 0);
+               mv64x60_write(bh, gt64260_64bit_windows[window].size_reg, 0);
+       }
+
+       return;
+}
+
+/*
+ * gt64260_disable_all_windows()
+ *
+ * The GT64260 has several windows that aren't represented in the table of
+ * windows at the top of this file.  This routine turns all of them off
+ * except for the memory controller windows, of course.
+ */
+static void __init
+gt64260_disable_all_windows(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       u32     i;
+
+       /* Disable 32bit windows (don't disable cpu->mem windows) */
+       for (i=MV64x60_CPU2DEV_0_WIN; i<MV64x60_32BIT_WIN_COUNT; i++) {
+               if (!(si->window_preserve_mask_32 & (1<<i)))
+                       gt64260_disable_window_32bit(bh, i);
+       }
+
+       /* Disable 64bit windows */
+       for (i=0; i<MV64x60_64BIT_WIN_COUNT; i++) {
+               if (!(si->window_preserve_mask_64 & (1<<i)))
+                       gt64260_disable_window_64bit(bh, i);
+       }
+
+       /* Turn off cpu protection windows not in gt64260_32bit_windows[] */
+       mv64x60_write(bh, GT64260_CPU_PROT_BASE_4, 0xfff);
+       mv64x60_write(bh, GT64260_CPU_PROT_SIZE_4, 0);
+       mv64x60_write(bh, GT64260_CPU_PROT_BASE_5, 0xfff);
+       mv64x60_write(bh, GT64260_CPU_PROT_SIZE_5, 0);
+       mv64x60_write(bh, GT64260_CPU_PROT_BASE_6, 0xfff);
+       mv64x60_write(bh, GT64260_CPU_PROT_SIZE_6, 0);
+       mv64x60_write(bh, GT64260_CPU_PROT_BASE_7, 0xfff);
+       mv64x60_write(bh, GT64260_CPU_PROT_SIZE_7, 0);
+
+       /* Turn off PCI->MEM access cntl wins not in gt64260_64bit_windows[] */
+       mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0xfff);
+       mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_HI, 0);
+       mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_SIZE, 0);
+       mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0xfff);
+       mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_HI, 0);
+       mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_SIZE, 0);
+       mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_LO, 0xfff);
+       mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_HI, 0);
+       mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_SIZE, 0);
+       mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_LO, 0xfff);
+       mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_HI, 0);
+       mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_SIZE, 0);
+
+       mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0xfff);
+       mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_HI, 0);
+       mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_SIZE, 0);
+       mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0xfff);
+       mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_HI, 0);
+       mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_SIZE, 0);
+       mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_LO, 0xfff);
+       mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_HI, 0);
+       mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_SIZE, 0);
+       mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_LO, 0xfff);
+       mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_HI, 0);
+       mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_SIZE, 0);
+
+       /* Disable all PCI-><whatever> windows */
+       mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x07ffffff);
+       mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x07ffffff);
+
+       return;
+}
+
+/*
+ * gt64260a_chip_specific_init()
+ *
+ * Implement errata work arounds for the GT64260A.
+ */
+static void
+gt64260a_chip_specific_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       struct ocp_device       *dev;
+       mv64x60_ocp_mpsc_data_t *mpsc_dp;
+       u8                      save_exclude;
+       u32                     val;
+
+       /* R#18 */
+       /* cpu read buffer to buffer 1 (reg 0x0448) */
+       mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26));
+
+       /* No longer errata so turn on */
+       /* Enable pci read/write combine, master write trigger,
+       * disable slave sync barrier
+       * readmultiple (reg 0x0c00 and 0x0c80)
+       */
+       if (si->pci_0.enable_bus) {
+               mv64x60_set_bits(bh, MV64x60_PCI0_CMD,
+                       ((1<<4) | (1<<5) | (1<<9) | (1<<13)));
+       }
+
+       if (si->pci_1.enable_bus) {
+               mv64x60_set_bits(bh, MV64x60_PCI1_CMD,
+                       ((1<<4) | (1<<5) | (1<<9) | (1<<13)));
+       }
+
+#if 1  /* XXXX */
+       /*
+        * Dave Wilhardt found that bit 4 in the PCI Command registers must
+        * be set if you are using cache coherency.
+        *
+        * Note: he also said that bit 4 must be on in all PCI devices but
+        *       that has not been implemented yet.
+        */
+       save_exclude = mv64x60_pci_exclude_bridge;
+       mv64x60_pci_exclude_bridge = FALSE;
+
+       early_read_config_dword(bh->hose_a,
+                       bh->hose_a->first_busno,
+                       PCI_DEVFN(0,0),
+                       PCI_COMMAND,
+                       &val);
+       val |= PCI_COMMAND_INVALIDATE;
+       early_write_config_dword(bh->hose_a,
+                       bh->hose_a->first_busno,
+                       PCI_DEVFN(0,0),
+                       PCI_COMMAND,
+                       val);
+
+       early_read_config_dword(bh->hose_b,
+                       bh->hose_b->first_busno,
+                       PCI_DEVFN(0,0),
+                       PCI_COMMAND,
+                       &val);
+       val |= PCI_COMMAND_INVALIDATE;
+       early_write_config_dword(bh->hose_b,
+                       bh->hose_b->first_busno,
+                       PCI_DEVFN(0,0),
+                       PCI_COMMAND,
+                       val);
+
+       mv64x60_pci_exclude_bridge = save_exclude;
+#endif
+
+       if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 0))
+                                                               != NULL) {
+               mpsc_dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions;
+               mpsc_dp->mirror_regs = 1;
+               mpsc_dp->cache_mgmt = 1;
+       }
+
+       if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 1))
+                                                               != NULL) {
+               mpsc_dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions;
+               mpsc_dp->mirror_regs = 1;
+               mpsc_dp->cache_mgmt = 1;
+       }
+
+       return;
+}
+
+/*
+ * gt64260b_chip_specific_init()
+ *
+ * Implement errata work arounds for the GT64260B.
+ */
+static void
+gt64260b_chip_specific_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       struct ocp_device       *dev;
+       mv64x60_ocp_mpsc_data_t *mpsc_dp;
+
+       /* R#18 */
+       /* cpu read buffer to buffer 1 (reg 0x0448) */
+       mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26));
+
+       /* No longer errata so turn on */
+       /* Enable pci read/write combine, master write trigger,
+       * disable slave sync barrier
+       * readmultiple (reg 0x0c00 and 0x0c80)
+       */
+       if (si->pci_0.enable_bus) {
+               mv64x60_set_bits(bh, MV64x60_PCI0_CMD,
+                       ((1<<4) | (1<<5) | (1<<9) | (1<<13)));
+       }
+
+       if (si->pci_1.enable_bus) {
+               mv64x60_set_bits(bh, MV64x60_PCI1_CMD,
+                       ((1<<4) | (1<<5) | (1<<9) | (1<<13)));
+       }
+
+       mv64x60_set_bits(bh, GT64260_CPU_WB_PRIORITY_BUFFER_DEPTH, 0xf);
+
+       /*
+        * The 64260B is not supposed to have the bug where the MPSC & ENET
+        * can't access cache coherent regions.  However, testing has shown
+        * that the MPSC, at least, still has this bug.
+        */
+       if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 0))
+                                                               != NULL) {
+               mpsc_dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions;
+               mpsc_dp->cache_mgmt = 1;
+       }
+
+       if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 1))
+                                                               != NULL) {
+               mpsc_dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions;
+               mpsc_dp->cache_mgmt = 1;
+       }
+
+       return;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     MV64360-Specific Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64360_translate_size()
+ *
+ * On the MV64360, the size register is set similar to the size you get
+ * from a pci config space BAR register.  That is, programmed from LSB to MSB
+ * as a sequence of 1's followed by a sequence of 0's. IOW, "size -1" with the
+ * assumption that the size is a power of 2.
+ */
+static u32 __init
+mv64360_translate_size(u32 base_addr, u32 size, u32 num_bits)
+{
+       return mv64x60_mask(size - 1, num_bits);
+}
+
+/*
+ * mv64360_untranslate_size()
+ *
+ * Translate the size register value of a window into a window size.
+ */
+static u32 __init
+mv64360_untranslate_size(u32 base_addr, u32 size, u32 num_bits)
+{
+       if (size > 0) {
+               size >>= (32 - num_bits);
+               size++;
+               size <<= (32 - num_bits);
+       }
+
+       return size;
+}
+
+/*
+ * mv64360_set_pci2mem_window()
+ *
+ * The PCI->MEM window registers are actually in PCI config space so need
+ * to set them by setting the correct config space BARs.
+ */
+static void __init
+mv64360_set_pci2mem_window(struct pci_controller *hose, u32 window, u32 base)
+{
+       struct {
+               u32     fcn;
+               u32     base_hi_bar;
+               u32     base_lo_bar;
+       } reg_addrs[] = {{ 0, 0x14, 0x10 }, { 0, 0x1c, 0x18 },
+               { 1, 0x14, 0x10 }, { 1, 0x1c, 0x18 }};
+
+       DBG("set pci->mem window: %d, hose: %d, base: 0x%x\n", window,
+               hose->index, base);
+
+       early_write_config_dword(hose, hose->first_busno,
+                       PCI_DEVFN(0, reg_addrs[window].fcn),
+                       reg_addrs[window].base_hi_bar, 0);
+       early_write_config_dword(hose, hose->first_busno,
+                       PCI_DEVFN(0, reg_addrs[window].fcn),
+                       reg_addrs[window].base_lo_bar,
+                       mv64x60_mask(base, 20) | 0xc);
+       return;
+}
+
+/*
+ * mv64360_is_enabled_32bit()
+ *
+ * On a MV64360, a window is enabled by either clearing a bit in the
+ * CPU BAR Enable reg or setting a bit in the window's base reg.
+ * Note that this doesn't work for windows on the PCI slave side but we don't
+ * check those so its okay.
+ */
+static u32 __init
+mv64360_is_enabled_32bit(mv64x60_handle_t *bh, u32 window)
+{
+       u32     rc = 0;
+
+       if ((mv64360_32bit_windows[window].base_reg != 0) &&
+               (mv64360_32bit_windows[window].size_reg != 0)) {
+
+               if (mv64360_32bit_windows[window].extra & 0x80000000) {
+                       rc = (mv64x60_read(bh,
+                               mv64360_32bit_windows[window].base_reg) & 
+                               (1 << (mv64360_32bit_windows[window].extra &
+                                                               0xff))) != 0;
+               }
+               else {
+                       rc = (mv64x60_read(bh, MV64360_CPU_BAR_ENABLE) & 
+                               (1 << mv64360_32bit_windows[window].extra)) ==0;
+               }
+       }
+
+       if (rc) {
+               DBG("32bit window %d is enabled\n", window);
+       }
+       else {
+               DBG("32bit window %d is disabled\n", window);
+       }
+
+       return rc;
+}
+
+/*
+ * mv64360_enable_window_32bit()
+ *
+ * On a MV64360, a window is enabled by either clearing a bit in the
+ * CPU BAR Enable reg or setting a bit in the window's base reg.
+ */
+static void __init
+mv64360_enable_window_32bit(mv64x60_handle_t *bh, u32 window)
+{
+       DBG("enable 32bit window: %d\n", window);
+
+       if ((mv64360_32bit_windows[window].base_reg != 0) &&
+               (mv64360_32bit_windows[window].size_reg != 0)) {
+
+               if (mv64360_32bit_windows[window].extra & 0x80000000) {
+                       mv64x60_set_bits(bh,
+                               mv64360_32bit_windows[window].base_reg,
+                               (1 << (mv64360_32bit_windows[window].extra &
+                                                                       0xff)));
+               }
+               else {
+                       mv64x60_clr_bits(bh, MV64360_CPU_BAR_ENABLE,
+                               (1 << mv64360_32bit_windows[window].extra));
+               }
+       }
+
+       return;
+}
+
+/*
+ * mv64360_disable_window_32bit()
+ *
+ * On a MV64360, a window is disabled by either setting a bit in the
+ * CPU BAR Enable reg or clearing a bit in the window's base reg.
+ */
+static void __init
+mv64360_disable_window_32bit(mv64x60_handle_t *bh, u32 window)
+{
+       DBG("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n",
+               window, mv64360_32bit_windows[window].base_reg,
+               mv64360_32bit_windows[window].size_reg);
+
+       if ((mv64360_32bit_windows[window].base_reg != 0) &&
+               (mv64360_32bit_windows[window].size_reg != 0)) {
+
+               if (mv64360_32bit_windows[window].extra & 0x80000000) {
+                       mv64x60_clr_bits(bh,
+                               mv64360_32bit_windows[window].base_reg,
+                               (1 << (mv64360_32bit_windows[window].extra &
+                                                                       0xff)));
+               }
+               else {
+                       mv64x60_set_bits(bh, MV64360_CPU_BAR_ENABLE,
+                               (1 << mv64360_32bit_windows[window].extra));
+               }
+       }
+
+       return;
+}
+
+/*
+ * mv64360_enable_window_64bit()
+ *
+ * On the MV64360, a 64-bit window is enabled by setting a bit in the window's 
+ * base reg.
+ */
+static void __init
+mv64360_enable_window_64bit(mv64x60_handle_t *bh, u32 window)
+{
+       DBG("enable 64bit window: %d\n", window);
+
+       /* For 64360, 'extra' field holds bit that enables the window */
+       if ((mv64360_64bit_windows[window].base_lo_reg!= 0) &&
+               (mv64360_64bit_windows[window].size_reg != 0)) {
+
+               if (mv64360_64bit_windows[window].extra & 0x80000000) {
+                       mv64x60_set_bits(bh,
+                               mv64360_64bit_windows[window].base_lo_reg,
+                               (1 << (mv64360_64bit_windows[window].extra &
+                                                                       0xff)));
+               } /* Should be no 'else' ones */
+       }
+
+       return;
+}
+
+/*
+ * mv64360_disable_window_64bit()
+ *
+ * On a MV64360, a 64-bit window is disabled by clearing a bit in the window's
+ * base reg.
+ */
+static void __init
+mv64360_disable_window_64bit(mv64x60_handle_t *bh, u32 window)
+{
+       DBG("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n",
+               window, mv64360_64bit_windows[window].base_lo_reg,
+               mv64360_64bit_windows[window].size_reg);
+
+       if ((mv64360_64bit_windows[window].base_lo_reg != 0) &&
+               (mv64360_64bit_windows[window].size_reg != 0)) {
+
+               if (mv64360_64bit_windows[window].extra & 0x80000000) {
+                       mv64x60_clr_bits(bh,
+                               mv64360_64bit_windows[window].base_lo_reg,
+                               (1 << (mv64360_64bit_windows[window].extra &
+                                                                       0xff)));
+               } /* Should be no 'else' ones */
+       }
+
+       return;
+}
+
+/*
+ * mv64360_disable_all_windows()
+ *
+ * The MV64360 has a few windows that aren't represented in the table of
+ * windows at the top of this file.  This routine turns all of them off
+ * except for the memory controller windows, of course.
+ */
+static void __init
+mv64360_disable_all_windows(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       u32     i;
+
+       /* Disable 32bit windows (don't disable cpu->mem windows) */
+       for (i=MV64x60_CPU2DEV_0_WIN; i<MV64x60_32BIT_WIN_COUNT; i++) {
+               if (!(si->window_preserve_mask_32 & (1<<i)))
+                       mv64360_disable_window_32bit(bh, i);
+       }
+
+       /* Disable 64bit windows */
+       for (i=0; i<MV64x60_64BIT_WIN_COUNT; i++) {
+               if (!(si->window_preserve_mask_64 & (1<<i)))
+                       mv64360_disable_window_64bit(bh, i);
+       }
+
+       /* Turn off PCI->MEM access cntl wins not in mv64360_64bit_windows[] */
+       mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0);
+       mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0);
+       mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0);
+       mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0);
+
+       /* Disable all PCI-><whatever> windows */
+       mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x0000f9ff);
+       mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x0000f9ff);
+
+       return;
+}
+
+/*
+ * mv64360_chip_specific_init()
+ *
+ * No errata work arounds for the MV64360 implemented at this point.
+ */
+static void
+mv64360_chip_specific_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       struct ocp_device       *dev;
+       mv64x60_ocp_mpsc_data_t *mpsc_dp;
+
+       if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 0))
+                                                               != NULL) {
+               mpsc_dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions;
+               mpsc_dp->brg_can_tune = 1;
+       }
+
+       if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 1))
+                                                               != NULL) {
+               mpsc_dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions;
+               mpsc_dp->brg_can_tune = 1;
+       }
+
+       return;
+}
+
+/*
+ * mv64460_chip_specific_init()
+ *
+ * No errata work arounds for the MV64460 implemented at this point.
+ */
+static void
+mv64460_chip_specific_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si)
+{
+       mv64360_chip_specific_init(bh, si); /* XXXX check errata */
+       return;
+}
diff --git a/arch/ppc/syslib/mv64x60_ocp.c b/arch/ppc/syslib/mv64x60_ocp.c
new file mode 100644 (file)
index 0000000..935bb9a
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * arch/ppc/syslib/mv64x60_ocp.c
+ * 
+ * Common OCP definitions for the Marvell GT64260/MV64360/MV64460/...
+ * line of host bridges.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2004 (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/init.h>
+#include <asm/mv64x60.h>
+#include <asm/ocp.h>
+
+static mv64x60_ocp_mpsc_data_t mv64x60_ocp_mpsc0_def = {
+       .mirror_regs            = 0,
+       .cache_mgmt             = 0,
+       .max_idle               = 0,
+       .default_baud           = 9600,
+       .default_bits           = 8,
+       .default_parity         = 'n',
+       .default_flow           = 'n',
+       .chr_1_val              = 0x00000000,
+       .chr_2_val              = 0x00000000,
+       .chr_10_val             = 0x00000003,
+       .mpcr_val               = 0,
+       .mrr_val                = 0x3ffffe38,
+       .rcrr_val               = 0,
+       .tcrr_val               = 0,
+       .intr_mask_val          = 0,
+       .bcr_val                = 0,
+       .sdma_irq               = MV64x60_IRQ_SDMA_0,
+       .brg_can_tune           = 0,
+       .brg_clk_src            = 8,            /* Default to TCLK */
+       .brg_clk_freq           = 100000000,    /* Default to 100 MHz */
+};
+static mv64x60_ocp_mpsc_data_t mv64x60_ocp_mpsc1_def = {
+       .mirror_regs            = 0,
+       .cache_mgmt             = 0,
+       .max_idle               = 0,
+       .default_baud           = 9600,
+       .default_bits           = 8,
+       .default_parity         = 'n',
+       .default_flow           = 'n',
+       .chr_1_val              = 0x00000000,
+       .chr_1_val              = 0x00000000,
+       .chr_2_val              = 0x00000000,
+       .chr_10_val             = 0x00000003,
+       .mpcr_val               = 0,
+       .mrr_val                = 0x3ffffe38,
+       .rcrr_val               = 0,
+       .tcrr_val               = 0,
+       .intr_mask_val          = 0,
+       .bcr_val                = 0,
+       .sdma_irq               = MV64x60_IRQ_SDMA_1,
+       .brg_can_tune           = 0,
+       .brg_clk_src            = 8,            /* Default to TCLK */
+       .brg_clk_freq           = 100000000,    /* Default to 100 MHz */
+};
+MV64x60_OCP_SYSFS_MPSC_DATA()
+
+struct ocp_def core_ocp[] = {
+       /* Base address for the block of bridge's regs */
+       { .vendor       = OCP_VENDOR_MARVELL,                   /* 0x00 */
+         .function     = OCP_FUNC_HB,
+         .index        = 0,
+         .paddr        = 0,
+         .pm           = OCP_CPM_NA,
+       },
+       /* 10/100 Ethernet controller */
+       { .vendor       = OCP_VENDOR_MARVELL,                   /* 0x01 */
+         .function     = OCP_FUNC_EMAC,
+         .index        = 0,
+         .paddr        = GT64260_ENET_0_OFFSET,
+         .irq          = MV64x60_IRQ_ETH_0,
+         .pm           = OCP_CPM_NA,
+       },
+       { .vendor       = OCP_VENDOR_MARVELL,                   /* 0x02 */
+         .function     = OCP_FUNC_EMAC,
+         .index        = 1,
+         .paddr        = GT64260_ENET_1_OFFSET,
+         .irq          = MV64x60_IRQ_ETH_1,
+         .pm           = OCP_CPM_NA,
+       },
+       { .vendor       = OCP_VENDOR_MARVELL,                   /* 0x03 */
+         .function     = OCP_FUNC_EMAC,
+         .index        = 2,
+         .paddr        = GT64260_ENET_2_OFFSET,
+         .irq          = MV64x60_IRQ_ETH_2,
+         .pm           = OCP_CPM_NA,
+       },
+       /* Multi-Protocol Serial Controller (MPSC) */
+       { .vendor       = OCP_VENDOR_MARVELL,                   /* 0x04 */
+         .function     = OCP_FUNC_MPSC,
+         .index        = 0,
+         .paddr        = MV64x60_MPSC_0_OFFSET,
+         .irq          = MV64x60_IRQ_MPSC_0,
+         .pm           = OCP_CPM_NA,
+         .additions    = &mv64x60_ocp_mpsc0_def,
+         .show         = &mv64x60_ocp_show_mpsc
+       },
+       { .vendor       = OCP_VENDOR_MARVELL,                   /* 0x05 */
+         .function     = OCP_FUNC_MPSC,
+         .index        = 1,
+         .paddr        = MV64x60_MPSC_1_OFFSET,
+         .irq          = MV64x60_IRQ_MPSC_1,
+         .pm           = OCP_CPM_NA,
+         .additions    = &mv64x60_ocp_mpsc1_def,
+         .show         = &mv64x60_ocp_show_mpsc
+       },
+       /* Inter-Integrated Circuit Controller */
+       { .vendor       = OCP_VENDOR_MARVELL,                   /* 0x06 */
+         .function     = OCP_FUNC_I2C,
+         .index        = 0,
+         .paddr        = GT64260_I2C_OFFSET,
+         .irq          = MV64x60_IRQ_I2C,
+         .pm           = OCP_CPM_NA,
+       },
+       /* Programmable Interrupt Controller */
+       { .vendor       = OCP_VENDOR_MARVELL,                   /* 0x07 */
+         .function     = OCP_FUNC_PIC,
+         .index        = 0,
+         .paddr        = GT64260_IC_OFFSET,
+         .pm           = OCP_CPM_NA,
+       },
+       { .vendor       = OCP_VENDOR_INVALID
+       }
+};
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c
new file mode 100644 (file)
index 0000000..d1a7856
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Common Flash Interface support:
+ *   Generic utility functions not dependant on command set
+ *
+ * Copyright (C) 2002 Red Hat
+ * Copyright (C) 2003 STMicroelectronics Limited
+ *
+ * This code is covered by the GPL.
+ *
+ * $Id: cfi_util.c,v 1.4 2004/07/14 08:38:44 dwmw2 Exp $
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/compatmac.h>
+
+struct cfi_extquery *
+cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       __u32 base = 0; // cfi->chips[0].start;
+       int ofs_factor = cfi->interleave * cfi->device_type;
+       int i;
+       struct cfi_extquery *extp = NULL;
+
+       printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
+       if (!adr)
+               goto out;
+
+       /* Switch it into Query Mode */
+       cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+
+       extp = kmalloc(size, GFP_KERNEL);
+       if (!extp) {
+               printk(KERN_ERR "Failed to allocate memory\n");
+               goto out;
+       }
+               
+       /* Read in the Extended Query Table */
+       for (i=0; i<size; i++) {
+               ((unsigned char *)extp)[i] = 
+                       cfi_read_query(map, base+((adr+i)*ofs_factor));
+       }
+
+       if (extp->MajorVersion != '1' || 
+           (extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
+               printk(KERN_WARNING "  Unknown %s Extended Query "
+                      "version %c.%c.\n",  name, extp->MajorVersion,
+                      extp->MinorVersion);
+               kfree(extp);
+               extp = NULL;
+               goto out;
+       }
+
+out:
+       /* Make sure it's in read mode */
+       cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL);
+
+       return extp;
+}
+
+EXPORT_SYMBOL(cfi_read_pri);
+
+void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       struct cfi_fixup *f;
+
+       for (f=fixups; f->fixup; f++) {
+               if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
+                   ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
+                       f->fixup(map, f->param);
+               }
+       }
+}
+
+EXPORT_SYMBOL(cfi_fixup);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
new file mode 100644 (file)
index 0000000..5f66e9b
--- /dev/null
@@ -0,0 +1,362 @@
+/**
+ *
+ * $Id: phram.c,v 1.1 2003/08/21 17:52:30 joern Exp $
+ *
+ * Copyright (c) Jochen Schaeuble <psionic@psionic.de>
+ * 07/2003     rewritten by Joern Engel <joern@wh.fh-wedel.de>
+ *
+ * DISCLAIMER:  This driver makes use of Rusty's excellent module code,
+ * so it will not work for 2.4 without changes and it wont work for 2.4
+ * as a module without major changes.  Oh well!
+ *
+ * Usage:
+ *
+ * one commend line parameter per device, each in the form:
+ *   phram=<name>,<start>,<len>
+ * <name> may be up to 63 characters.
+ * <start> and <len> can be octal, decimal or hexadecimal.  If followed
+ * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or
+ * gigabytes.
+ *
+ */
+
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mtd/mtd.h>
+
+#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
+
+struct phram_mtd_list {
+       struct list_head list;
+       struct mtd_info *mtdinfo;
+};
+
+static LIST_HEAD(phram_list);
+
+
+
+int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       u_char *start = (u_char *)mtd->priv;
+
+       if (instr->addr + instr->len > mtd->size)
+               return -EINVAL;
+       
+       memset(start + instr->addr, 0xff, instr->len);
+
+       /* This'll catch a few races. Free the thing before returning :) 
+        * I don't feel at all ashamed. This kind of thing is possible anyway
+        * with flash, but unlikely.
+        */
+
+       instr->state = MTD_ERASE_DONE;
+
+       if (instr->callback)
+               (*(instr->callback))(instr);
+       else
+               kfree(instr);
+
+       return 0;
+}
+
+int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char **mtdbuf)
+{
+       u_char *start = (u_char *)mtd->priv;
+
+       if (from + len > mtd->size)
+               return -EINVAL;
+       
+       *mtdbuf = start + from;
+       *retlen = len;
+       return 0;
+}
+
+void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
+{
+}
+
+int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char *buf)
+{
+       u_char *start = (u_char *)mtd->priv;
+
+       if (from + len > mtd->size)
+               return -EINVAL;
+       
+       memcpy(buf, start + from, len);
+
+       *retlen = len;
+       return 0;
+}
+
+int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
+               size_t *retlen, const u_char *buf)
+{
+       u_char *start = (u_char *)mtd->priv;
+
+       if (to + len > mtd->size)
+               return -EINVAL;
+       
+       memcpy(start + to, buf, len);
+
+       *retlen = len;
+       return 0;
+}
+
+
+
+static void unregister_devices(void)
+{
+       struct phram_mtd_list *this;
+
+       list_for_each_entry(this, &phram_list, list) {
+               del_mtd_device(this->mtdinfo);
+               iounmap(this->mtdinfo->priv);
+               kfree(this->mtdinfo);
+               kfree(this);
+       }
+}
+
+static int register_device(char *name, unsigned long start, unsigned long len)
+{
+       struct phram_mtd_list *new;
+       int ret = -ENOMEM;
+
+       new = kmalloc(sizeof(*new), GFP_KERNEL);
+       if (!new)
+               goto out0;
+
+       new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
+       if (!new->mtdinfo)
+               goto out1;
+       
+       memset(new->mtdinfo, 0, sizeof(struct mtd_info));
+
+       ret = -EIO;
+       new->mtdinfo->priv = ioremap(start, len);
+       if (!new->mtdinfo->priv) {
+               ERROR("ioremap failed\n");
+               goto out2;
+       }
+
+
+       new->mtdinfo->name = name;
+       new->mtdinfo->size = len;
+       new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
+        new->mtdinfo->erase = phram_erase;
+       new->mtdinfo->point = phram_point;
+       new->mtdinfo->unpoint = phram_unpoint;
+       new->mtdinfo->read = phram_read;
+       new->mtdinfo->write = phram_write;
+       new->mtdinfo->owner = THIS_MODULE;
+       new->mtdinfo->type = MTD_RAM;
+       new->mtdinfo->erasesize = 0x0;
+
+       ret = -EAGAIN;
+       if (add_mtd_device(new->mtdinfo)) {
+               ERROR("Failed to register new device\n");
+               goto out3;
+       }
+
+       list_add_tail(&new->list, &phram_list);
+       return 0;       
+
+out3:
+       iounmap(new->mtdinfo->priv);
+out2:
+       kfree(new->mtdinfo);
+out1:
+       kfree(new);
+out0:
+       return ret;
+}
+
+static int ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+       unsigned long result = simple_strtoul(cp, endp, base);
+
+       switch (**endp) {
+       case 'G':
+               result *= 1024;
+       case 'M':
+               result *= 1024;
+       case 'k':
+               result *= 1024;
+               endp++;
+       }
+       return result;
+}
+
+static int parse_num32(uint32_t *num32, const char *token)
+{
+       char *endp;
+       unsigned long n;
+
+       n = ustrtoul(token, &endp, 0);
+       if (*endp)
+               return -EINVAL;
+
+       *num32 = n;
+       return 0;
+}
+
+static int parse_name(char **pname, const char *token)
+{
+       size_t len;
+       char *name;
+
+       len = strlen(token) + 1;
+       if (len > 64)
+               return -ENOSPC;
+
+       name = kmalloc(len, GFP_KERNEL);
+       if (!name)
+               return -ENOMEM;
+
+       strcpy(name, token);
+
+       *pname = name;
+       return 0;
+}
+
+#define parse_err(fmt, args...) do {   \
+       ERROR(fmt , ## args);   \
+       return 0;               \
+} while (0)
+
+static int phram_setup(const char *val, struct kernel_param *kp)
+{
+       char buf[64+12+12], *str = buf;
+       char *token[3];
+       char *name;
+       uint32_t start;
+       uint32_t len;
+       int i, ret;
+
+       if (strnlen(val, sizeof(str)) >= sizeof(str))
+               parse_err("parameter too long\n");
+
+       strcpy(str, val);
+
+       for (i=0; i<3; i++)
+               token[i] = strsep(&str, ",");
+
+       if (str)
+               parse_err("too many arguments\n");
+
+       if (!token[2])
+               parse_err("not enough arguments\n");
+
+       ret = parse_name(&name, token[0]);
+       if (ret == -ENOMEM)
+               parse_err("out of memory\n");
+       if (ret == -ENOSPC)
+               parse_err("name too long\n");
+       if (ret)
+               return 0;
+
+       ret = parse_num32(&start, token[1]);
+       if (ret)
+               parse_err("illegal start address\n");
+
+       ret = parse_num32(&len, token[2]);
+       if (ret)
+               parse_err("illegal device length\n");
+
+       register_device(name, start, len);
+
+       return 0;
+}
+
+module_param_call(phram, phram_setup, NULL, NULL, 000);
+MODULE_PARM_DESC(phram, "Memory region to map. \"map=<name>,<start><length>\"");
+
+/*
+ * Just for compatibility with slram, this is horrible and should go someday.
+ */
+static int __init slram_setup(const char *val, struct kernel_param *kp)
+{
+       char buf[256], *str = buf;
+
+       if (!val || !val[0])
+               parse_err("no arguments to \"slram=\"\n");
+
+       if (strnlen(val, sizeof(str)) >= sizeof(str))
+               parse_err("parameter too long\n");
+
+       strcpy(str, val);
+
+       while (str) {
+               char *token[3];
+               char *name;
+               uint32_t start;
+               uint32_t len;
+               int i, ret;
+
+               for (i=0; i<3; i++) {
+                       token[i] = strsep(&str, ",");
+                       if (token[i])
+                               continue;
+                       parse_err("wrong number of arguments to \"slram=\"\n");
+               }
+
+               /* name */
+               ret = parse_name(&name, token[0]);
+               if (ret == -ENOMEM)
+                       parse_err("of memory\n");
+               if (ret == -ENOSPC)
+                       parse_err("too long\n");
+               if (ret)
+                       return 1;
+
+               /* start */
+               ret = parse_num32(&start, token[1]);
+               if (ret)
+                       parse_err("illegal start address\n");
+
+               /* len */
+               if (token[2][0] == '+')
+                       ret = parse_num32(&len, token[2] + 1);
+               else
+                       ret = parse_num32(&len, token[2]);
+
+               if (ret)
+                       parse_err("illegal device length\n");
+
+               if (token[2][0] != '+') {
+                       if (len < start)
+                               parse_err("end < start\n");
+                       len -= start;
+               }
+
+               register_device(name, start, len);
+       }
+       return 1;
+}
+
+module_param_call(slram, slram_setup, NULL, NULL, 000);
+MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><length/end>\"");
+
+
+int __init init_phram(void)
+{
+       printk(KERN_ERR "phram loaded\n");
+       return 0;
+}
+
+static void __exit cleanup_phram(void)
+{
+       unregister_devices();
+}
+
+module_init(init_phram);
+module_exit(cleanup_phram);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
+MODULE_DESCRIPTION("MTD driver for physical RAM");
diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c
new file mode 100644 (file)
index 0000000..ec2612b
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * ichxrom.c
+ *
+ * Normal mappings of chips in physical memory
+ * $Id: ichxrom.c,v 1.7 2004/07/14 18:14:09 eric Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/mtd/cfi.h>
+
+#define xstr(s) str(s)
+#define str(s) #s
+#define MOD_NAME xstr(KBUILD_BASENAME)
+
+#define MTD_DEV_NAME_LENGTH 16
+
+#define RESERVE_MEM_REGION 0
+
+
+#define MANUFACTURER_INTEL     0x0089
+#define I82802AB       0x00ad
+#define I82802AC       0x00ac
+
+#define ICHX_FWH_REGION_START  0xFF000000UL
+#define ICHX_FWH_REGION_SIZE   0x01000000UL
+#define BIOS_CNTL      0x4e
+#define FWH_DEC_EN1    0xE3
+#define FWH_DEC_EN2    0xF0
+#define FWH_SEL1       0xE8
+#define FWH_SEL2       0xEE
+
+struct ichxrom_map_info {
+       struct map_info map;
+       struct mtd_info *mtd;
+       unsigned long window_addr;
+       struct pci_dev *pdev;
+       struct resource window_rsrc;
+       struct resource rom_rsrc;
+       char mtd_name[MTD_DEV_NAME_LENGTH];
+};
+
+static inline unsigned long addr(struct map_info *map, unsigned long ofs)
+{
+       unsigned long offset;
+       offset = ((8*1024*1024) - map->size) + ofs;
+       if (offset >= (4*1024*1024)) {
+               offset += 0x400000;
+       }
+       return map->map_priv_1 + 0x400000 + offset;
+}
+
+static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr)
+{
+       return addr - map->map_priv_1 + ICHX_FWH_REGION_START;
+}
+       
+static map_word ichxrom_read(struct map_info *map, unsigned long ofs)
+{
+       map_word val;
+       int i;
+       switch(map->bankwidth) {
+       case 1:  val.x[0] = __raw_readb(addr(map, ofs)); break;
+       case 2:  val.x[0] = __raw_readw(addr(map, ofs)); break;
+       case 4:  val.x[0] = __raw_readl(addr(map, ofs)); break;
+#if BITS_PER_LONG >= 64
+       case 8:  val.x[0] = __raw_readq(addr(map, ofs)); break;
+#endif
+       default: val.x[0] = 0; break;
+       }
+       for(i = 1; i < map_words(map); i++) {
+               val.x[i] = 0;
+       }
+       return val;
+}
+
+static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+       memcpy_fromio(to, addr(map, from), len);
+}
+
+static void ichxrom_write(struct map_info *map, map_word d, unsigned long ofs)
+{
+       switch(map->bankwidth) {
+       case 1: __raw_writeb(d.x[0], addr(map,ofs)); break;
+       case 2: __raw_writew(d.x[0], addr(map,ofs)); break;
+       case 4: __raw_writel(d.x[0], addr(map,ofs)); break;
+#if BITS_PER_LONG >= 64
+       case 8: __raw_writeq(d.x[0], addr(map,ofs)); break;
+#endif
+       }
+       mb();
+}
+
+static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+       memcpy_toio(addr(map, to), from, len);
+}
+
+static struct ichxrom_map_info ichxrom_map = {
+       .map = {
+               .name = MOD_NAME,
+               .phys = NO_XIP,
+               .size = 0,
+               .bankwidth = 1,
+               .read = ichxrom_read,
+               .copy_from = ichxrom_copy_from,
+               .write = ichxrom_write,
+               .copy_to = ichxrom_copy_to,
+               /* Firmware hubs only use vpp when being programmed
+                * in a factory setting.  So in-place programming
+                * needs to use a different method.
+                */
+       },
+       /* remaining fields of structure are initialized to 0 */
+};
+
+enum fwh_lock_state {
+       FWH_DENY_WRITE = 1,
+       FWH_IMMUTABLE  = 2,
+       FWH_DENY_READ  = 4,
+};
+
+static void ichxrom_cleanup(struct ichxrom_map_info *info)
+{
+       u16 word;
+
+       /* Disable writes through the rom window */
+       pci_read_config_word(info->pdev, BIOS_CNTL, &word);
+       pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1);
+
+       if (info->mtd) {
+               del_mtd_device(info->mtd);
+               map_destroy(info->mtd);
+               info->mtd = NULL;
+               info->map.virt = 0;
+       }
+       if (info->rom_rsrc.parent)
+               release_resource(&info->rom_rsrc);
+       if (info->window_rsrc.parent)
+               release_resource(&info->window_rsrc);
+
+       if (info->window_addr) {
+               iounmap((void *)(info->window_addr));
+               info->window_addr = 0;
+       }
+}
+
+
+static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len,
+       enum fwh_lock_state state)
+{
+       struct map_info *map = mtd->priv;
+       unsigned long start = ofs;
+       unsigned long end = start + len -1;
+
+       /* FIXME do I need to guard against concurrency here? */
+       /* round down to 64K boundaries */
+       start = start & ~0xFFFF;
+       end = end & ~0xFFFF;
+       while (start <= end) {
+               unsigned long ctrl_addr;
+               ctrl_addr = addr(map, start) - 0x400000 + 2;
+               writeb(state, ctrl_addr);
+               start = start + 0x10000;
+       }
+       return 0;
+}
+
+static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+       return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE);
+}
+
+static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+       return ichxrom_set_lock_state(mtd, ofs, len, 0);
+}
+
+static int __devinit ichxrom_init_one (struct pci_dev *pdev,
+       const struct pci_device_id *ent)
+{
+       u16 word;
+       struct ichxrom_map_info *info = &ichxrom_map;
+       unsigned long map_size;
+       static char *probes[] = { "cfi_probe", "jedec_probe" };
+       struct cfi_private *cfi;
+
+       /* For now I just handle the ichx and I assume there
+        * are not a lot of resources up at the top of the address
+        * space.  It is possible to handle other devices in the
+        * top 16MB but it is very painful.  Also since
+        * you can only really attach a FWH to an ICHX there
+        * a number of simplifications you can make.
+        *
+        * Also you can page firmware hubs if an 8MB window isn't enough 
+        * but don't currently handle that case either.
+        */
+
+       info->pdev = pdev;
+
+       /*
+        * Try to reserve the window mem region.  If this fails then
+        * it is likely due to the window being "reseved" by the BIOS.
+        */
+       info->window_rsrc.name = MOD_NAME;
+       info->window_rsrc.start = ICHX_FWH_REGION_START;
+       info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1;
+       info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+       if (request_resource(&iomem_resource, &info->window_rsrc)) {
+               info->window_rsrc.parent = NULL;
+               printk(KERN_ERR MOD_NAME
+                      " %s(): Unable to register resource"
+                      " 0x%.08lx-0x%.08lx - kernel bug?\n",
+                      __func__,
+                      info->window_rsrc.start, info->window_rsrc.end);
+       }
+       
+       /* Enable writes through the rom window */
+       pci_read_config_word(pdev, BIOS_CNTL, &word);
+       if (!(word & 1)  && (word & (1<<1))) {
+               /* The BIOS will generate an error if I enable
+                * this device, so don't even try.
+                */
+               printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
+               goto failed;
+       }
+       pci_write_config_word(pdev, BIOS_CNTL, word | 1);
+
+
+       /* Map the firmware hub into my address space. */
+       /* Does this use too much virtual address space? */
+       info->window_addr = (unsigned long)ioremap(
+               ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
+       if (!info->window_addr) {
+               printk(KERN_ERR "Failed to ioremap\n");
+               goto failed;
+       }
+
+       /* For now assume the firmware has setup all relevant firmware
+        * windows.  We don't have enough information to handle this case
+        * intelligently.
+        */
+
+       /* FIXME select the firmware hub and enable a window to it. */
+
+       info->mtd = NULL;
+       info->map.map_priv_1 = info->window_addr;
+
+       /* Loop through the possible bankwidths */
+       for(ichxrom_map.map.bankwidth = 4; ichxrom_map.map.bankwidth; ichxrom_map.map.bankwidth >>= 1) {
+               map_size = ICHX_FWH_REGION_SIZE;
+               while(!info->mtd && (map_size > 0)) {
+                       int i;
+                       info->map.size = map_size;
+                       for(i = 0; i < sizeof(probes)/sizeof(char *); i++) {
+                               info->mtd = do_map_probe(probes[i], &ichxrom_map.map);
+                               if (info->mtd)
+                                       break;
+                       }
+                       map_size -= 512*1024;
+               }
+               if (info->mtd)
+                       break;
+       }
+       if (!info->mtd) {
+               goto failed;
+       }
+       cfi = ichxrom_map.map.fldrv_priv;
+       if ((cfi->mfr == MANUFACTURER_INTEL) && (
+                   (cfi->id == I82802AB) ||
+                   (cfi->id == I82802AC))) 
+       {
+               /* If it is a firmware hub put in the special lock
+                * and unlock routines.
+                */
+               info->mtd->lock = ichxrom_lock;
+               info->mtd->unlock = ichxrom_unlock;
+       }
+       if (info->mtd->size > info->map.size) {
+               printk(KERN_WARNING MOD_NAME " rom(%u) larger than window(%u). fixing...\n",
+                      info->mtd->size, info->map.size);
+               info->mtd->size = info->map.size;
+       }
+               
+       info->mtd->owner = THIS_MODULE;
+       add_mtd_device(info->mtd);
+
+       if (info->window_rsrc.parent) {
+               /*
+                * Registering the MTD device in iomem may not be possible
+                * if there is a BIOS "reserved" and BUSY range.  If this
+                * fails then continue anyway.
+                */
+               snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
+                        "mtd%d", info->mtd->index);
+
+               info->rom_rsrc.name = info->mtd_name;
+               info->rom_rsrc.start = ICHX_FWH_REGION_START
+                       + ICHX_FWH_REGION_SIZE - map_size;
+               info->rom_rsrc.end = ICHX_FWH_REGION_START
+                       + ICHX_FWH_REGION_SIZE;
+               info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+               if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
+                       printk(KERN_ERR MOD_NAME
+                              ": cannot reserve MTD resource\n");
+                       info->rom_rsrc.parent = NULL;
+               }
+       }
+
+       return 0;
+
+ failed:
+       ichxrom_cleanup(info);
+       return -ENODEV;
+}
+
+
+static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
+{
+       struct ichxrom_map_info *info = &ichxrom_map;
+       u16 word;
+
+       del_mtd_device(info->mtd);
+       map_destroy(info->mtd);
+       info->mtd = NULL;
+       info->map.map_priv_1 = 0;
+
+       iounmap((void *)(info->window_addr));
+       info->window_addr = 0;
+
+       /* Disable writes through the rom window */
+       pci_read_config_word(pdev, BIOS_CNTL, &word);
+       pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
+
+#if RESERVE_MEM_REGION 
+       release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
+#endif
+}
+
+static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, 
+         PCI_ANY_ID, PCI_ANY_ID, },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, 
+         PCI_ANY_ID, PCI_ANY_ID, },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, 
+         PCI_ANY_ID, PCI_ANY_ID, },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
+         PCI_ANY_ID, PCI_ANY_ID, },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
+         PCI_ANY_ID, PCI_ANY_ID, },
+       { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl);
+
+#if 0
+static struct pci_driver ichxrom_driver = {
+       .name =         MOD_NAME,
+       .id_table =     ichxrom_pci_tbl,
+       .probe =        ichxrom_init_one,
+       .remove =       ichxrom_remove_one,
+};
+#endif
+
+static struct pci_dev *mydev;
+int __init init_ichxrom(void)
+{
+       struct pci_dev *pdev;
+       struct pci_device_id *id;
+
+       pdev = NULL;
+       for (id = ichxrom_pci_tbl; id->vendor; id++) {
+               pdev = pci_find_device(id->vendor, id->device, NULL);
+               if (pdev) {
+                       break;
+               }
+       }
+       if (pdev) {
+               mydev = pdev;
+               return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
+       }
+       return -ENXIO;
+#if 0
+       return pci_module_init(&ichxrom_driver);
+#endif
+}
+
+static void __exit cleanup_ichxrom(void)
+{
+       ichxrom_remove_one(mydev);
+}
+
+module_init(init_ichxrom);
+module_exit(cleanup_ichxrom);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>");
+MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge");
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
new file mode 100644 (file)
index 0000000..677c216
--- /dev/null
@@ -0,0 +1,1237 @@
+/* 
+ * drivers/mtd/nand/diskonchip.c
+ *
+ * (C) 2003 Red Hat, Inc.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ * Interface to generic NAND code for M-Systems DiskOnChip devices
+ *
+ * $Id: diskonchip.c,v 1.23 2004/07/13 00:14:35 dbrown Exp $
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/doc2000.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/inftl.h>
+
+/* Where to look for the devices? */
+#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
+#define CONFIG_MTD_DOCPROBE_ADDRESS 0
+#endif
+
+static unsigned long __initdata doc_locations[] = {
+#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
+#ifdef CONFIG_MTD_DOCPROBE_HIGH
+       0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 
+       0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
+       0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 
+       0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, 
+       0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
+#else /*  CONFIG_MTD_DOCPROBE_HIGH */
+       0xc8000, 0xca000, 0xcc000, 0xce000, 
+       0xd0000, 0xd2000, 0xd4000, 0xd6000,
+       0xd8000, 0xda000, 0xdc000, 0xde000, 
+       0xe0000, 0xe2000, 0xe4000, 0xe6000, 
+       0xe8000, 0xea000, 0xec000, 0xee000,
+#endif /*  CONFIG_MTD_DOCPROBE_HIGH */
+#elif defined(__PPC__)
+       0xe4000000,
+#elif defined(CONFIG_MOMENCO_OCELOT)
+       0x2f000000,
+        0xff000000,
+#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
+        0xff000000,
+##else
+#warning Unknown architecture for DiskOnChip. No default probe locations defined
+#endif
+       0xffffffff };
+
+static struct mtd_info *doclist = NULL;
+
+struct doc_priv {
+       unsigned long virtadr;
+       unsigned long physadr;
+       u_char ChipID;
+       u_char CDSNControl;
+       int chips_per_floor; /* The number of chips detected on each floor */
+       int curfloor;
+       int curchip;
+       int mh0_page;
+       int mh1_page;
+       struct mtd_info *nextdoc;
+};
+
+/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL
+   MediaHeader.  The spec says to just keep going, I think, but that's just
+   silly. */
+#define MAX_MEDIAHEADER_SCAN 8
+
+/* This is the syndrome computed by the HW ecc generator upon reading an empty
+   page, one with all 0xff for data and stored ecc code. */
+static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
+/* This is the ecc value computed by the HW ecc generator upon writing an empty
+   page, one with all 0xff for data. */
+static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
+
+#define INFTL_BBT_RESERVED_BLOCKS 4
+
+#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
+#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
+
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
+static void doc200x_select_chip(struct mtd_info *mtd, int chip);
+
+static int debug=0;
+MODULE_PARM(debug, "i");
+
+static int try_dword=1;
+MODULE_PARM(try_dword, "i");
+
+static int no_ecc_failures=0;
+MODULE_PARM(no_ecc_failures, "i");
+
+static int no_autopart=0;
+MODULE_PARM(no_autopart, "i");
+
+#ifdef MTD_NAND_DISKONCHIP_BBTWRITE
+static int inftl_bbt_write=1;
+#else
+static int inftl_bbt_write=0;
+#endif
+MODULE_PARM(inftl_bbt_write, "i");
+
+static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
+MODULE_PARM(doc_config_location, "l");
+MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
+
+static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
+{
+       volatile char dummy;
+       int i;
+       
+       for (i = 0; i < cycles; i++) {
+               if (DoC_is_Millennium(doc))
+                       dummy = ReadDOC(doc->virtadr, NOP);
+               else
+                       dummy = ReadDOC(doc->virtadr, DOCStatus);
+       }
+       
+}
+/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+static int _DoC_WaitReady(struct doc_priv *doc)
+{
+       unsigned long docptr = doc->virtadr;
+       unsigned long timeo = jiffies + (HZ * 10);
+
+       if(debug) printk("_DoC_WaitReady...\n");
+       /* Out-of-line routine to wait for chip response */
+       while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+               if (time_after(jiffies, timeo)) {
+                       printk("_DoC_WaitReady timed out.\n");
+                       return -EIO;
+               }
+               udelay(1);
+               cond_resched();
+       }
+
+       return 0;
+}
+
+static inline int DoC_WaitReady(struct doc_priv *doc)
+{
+       unsigned long docptr = doc->virtadr;
+       int ret = 0;
+
+       DoC_Delay(doc, 4);
+
+       if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
+               /* Call the out-of-line routine to wait */
+               ret = _DoC_WaitReady(doc);
+
+       DoC_Delay(doc, 2);
+       if(debug) printk("DoC_WaitReady OK\n");
+       return ret;
+}
+
+static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+
+       if(debug)printk("write_byte %02x\n", datum);
+       WriteDOC(datum, docptr, CDSNSlowIO);
+       WriteDOC(datum, docptr, 2k_CDSN_IO);
+}
+
+static u_char doc2000_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       u_char ret;
+
+       ReadDOC(docptr, CDSNSlowIO);
+       DoC_Delay(doc, 2);
+       ret = ReadDOC(docptr, 2k_CDSN_IO);
+       if (debug) printk("read_byte returns %02x\n", ret);
+       return ret;
+}
+
+static void doc2000_writebuf(struct mtd_info *mtd, 
+                            const u_char *buf, int len)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int i;
+       if (debug)printk("writebuf of %d bytes: ", len);
+       for (i=0; i < len; i++) {
+               WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
+               if (debug && i < 16)
+                       printk("%02x ", buf[i]);
+       }
+       if (debug) printk("\n");
+}
+
+static void doc2000_readbuf(struct mtd_info *mtd, 
+                           u_char *buf, int len)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int i;
+
+       if (debug)printk("readbuf of %d bytes: ", len);
+
+       for (i=0; i < len; i++) {
+               buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
+       }
+}
+
+static void doc2000_readbuf_dword(struct mtd_info *mtd, 
+                           u_char *buf, int len)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int i;
+
+       if (debug) printk("readbuf_dword of %d bytes: ", len);
+
+       if (unlikely((((unsigned long)buf)|len) & 3)) {
+               for (i=0; i < len; i++) {
+                       *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
+               }
+       } else {
+               for (i=0; i < len; i+=4) {
+                       *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
+               }
+       }
+}
+
+static int doc2000_verifybuf(struct mtd_info *mtd, 
+                             const u_char *buf, int len)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int i;
+
+       for (i=0; i < len; i++)
+               if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
+                       return -EFAULT;
+       return 0;
+}
+
+static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       uint16_t ret;
+
+       doc200x_select_chip(mtd, nr);
+       doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
+       this->write_byte(mtd, NAND_CMD_READID);
+       doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
+       doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
+       this->write_byte(mtd, 0);
+       doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+
+       ret = this->read_byte(mtd) << 8;
+       ret |= this->read_byte(mtd);
+
+       if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) {
+               /* First chip probe. See if we get same results by 32-bit access */
+               union {
+                       uint32_t dword;
+                       uint8_t byte[4];
+               } ident;
+               unsigned long docptr = doc->virtadr;
+
+               doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
+               doc2000_write_byte(mtd, NAND_CMD_READID);
+               doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
+               doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
+               doc2000_write_byte(mtd, 0);
+               doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+
+               ident.dword = readl(docptr + DoC_2k_CDSN_IO);
+               if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
+                       printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
+                       this->read_buf = &doc2000_readbuf_dword;
+               }
+       }
+               
+       return ret;
+}
+
+static void __init doc2000_count_chips(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       uint16_t mfrid;
+       int i;
+
+       /* Max 4 chips per floor on DiskOnChip 2000 */
+       doc->chips_per_floor = 4;
+
+       /* Find out what the first chip is */
+       mfrid = doc200x_ident_chip(mtd, 0);
+
+       /* Find how many chips in each floor. */
+       for (i = 1; i < 4; i++) {
+               if (doc200x_ident_chip(mtd, i) != mfrid)
+                       break;
+       }
+       doc->chips_per_floor = i;
+       printk(KERN_DEBUG "Detected %d chips per floor.\n", i);
+}
+
+static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+{
+       struct doc_priv *doc = (void *)this->priv;
+
+       int status;
+       
+       DoC_WaitReady(doc);
+       this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+       DoC_WaitReady(doc);
+       status = (int)this->read_byte(mtd);
+
+       return status;
+}
+
+static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+
+       WriteDOC(datum, docptr, CDSNSlowIO);
+       WriteDOC(datum, docptr, Mil_CDSN_IO);
+       WriteDOC(datum, docptr, WritePipeTerm);
+}
+
+static u_char doc2001_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+
+       //ReadDOC(docptr, CDSNSlowIO);
+       /* 11.4.5 -- delay twice to allow extended length cycle */
+       DoC_Delay(doc, 2);
+       ReadDOC(docptr, ReadPipeInit);
+       //return ReadDOC(docptr, Mil_CDSN_IO);
+       return ReadDOC(docptr, LastDataRead);
+}
+
+static void doc2001_writebuf(struct mtd_info *mtd, 
+                            const u_char *buf, int len)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int i;
+
+       for (i=0; i < len; i++)
+               WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
+       /* Terminate write pipeline */
+       WriteDOC(0x00, docptr, WritePipeTerm);
+}
+
+static void doc2001_readbuf(struct mtd_info *mtd, 
+                           u_char *buf, int len)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int i;
+
+       /* Start read pipeline */
+       ReadDOC(docptr, ReadPipeInit);
+
+       for (i=0; i < len-1; i++)
+               buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+
+       /* Terminate read pipeline */
+       buf[i] = ReadDOC(docptr, LastDataRead);
+}
+
+static int doc2001_verifybuf(struct mtd_info *mtd, 
+                            const u_char *buf, int len)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int i;
+
+       /* Start read pipeline */
+       ReadDOC(docptr, ReadPipeInit);
+
+       for (i=0; i < len-1; i++)
+               if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
+                       ReadDOC(docptr, LastDataRead);
+                       return i;
+               }
+       if (buf[i] != ReadDOC(docptr, LastDataRead))
+               return i;
+       return 0;
+}
+
+static void doc200x_select_chip(struct mtd_info *mtd, int chip)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int floor = 0;
+
+       /* 11.4.4 -- deassert CE before changing chip */
+       doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
+
+       if(debug)printk("select chip (%d)\n", chip);
+
+       if (chip == -1)
+               return;
+
+       floor = chip / doc->chips_per_floor;
+       chip -= (floor *  doc->chips_per_floor);
+
+       WriteDOC(floor, docptr, FloorSelect);
+       WriteDOC(chip, docptr, CDSNDeviceSelect);
+
+       doc200x_hwcontrol(mtd, NAND_CTL_SETNCE);
+
+       doc->curchip = chip;
+       doc->curfloor = floor;
+}
+
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+
+       switch(cmd) {
+       case NAND_CTL_SETNCE:
+               doc->CDSNControl |= CDSN_CTRL_CE;
+               break;
+       case NAND_CTL_CLRNCE:
+               doc->CDSNControl &= ~CDSN_CTRL_CE;
+               break;
+       case NAND_CTL_SETCLE:
+               doc->CDSNControl |= CDSN_CTRL_CLE;
+               break;
+       case NAND_CTL_CLRCLE:
+               doc->CDSNControl &= ~CDSN_CTRL_CLE;
+               break;
+       case NAND_CTL_SETALE:
+               doc->CDSNControl |= CDSN_CTRL_ALE;
+               break;
+       case NAND_CTL_CLRALE:
+               doc->CDSNControl &= ~CDSN_CTRL_ALE;
+               break;
+       case NAND_CTL_SETWP:
+               doc->CDSNControl |= CDSN_CTRL_WP;
+               break;
+       case NAND_CTL_CLRWP:
+               doc->CDSNControl &= ~CDSN_CTRL_WP;
+               break;
+       }
+       if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
+       WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+       /* 11.4.3 -- 4 NOPs after CSDNControl write */
+       DoC_Delay(doc, 4);
+}
+
+static int doc200x_dev_ready(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+
+       /* 11.4.2 -- must NOP four times before checking FR/B# */
+       DoC_Delay(doc, 4);
+       if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+               if(debug)
+                       printk("not ready\n");
+               return 0;
+       }
+       /* 11.4.2 -- Must NOP twice if it's ready */
+       DoC_Delay(doc, 2);
+       if (debug)printk("was ready\n");
+       return 1; 
+}      
+
+static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+       /* This is our last resort if we couldn't find or create a BBT.  Just
+          pretend all blocks are good. */
+       return 0;
+}
+
+static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+
+       /* Prime the ECC engine */
+       switch(mode) {
+       case NAND_ECC_READ:
+               WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+               WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+               break;
+       case NAND_ECC_WRITE:
+               WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+               WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+               break;
+       }       
+}
+
+/* This code is only called on write */
+static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+                                unsigned char *ecc_code)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       int i;
+       int emptymatch = 1;
+
+       /* flush the pipeline */
+       if (DoC_is_2000(doc)) {
+               WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl);
+               WriteDOC(0, docptr, 2k_CDSN_IO);
+               WriteDOC(0, docptr, 2k_CDSN_IO);
+               WriteDOC(0, docptr, 2k_CDSN_IO);
+               WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+       } else {
+               WriteDOC(0, docptr, NOP);
+               WriteDOC(0, docptr, NOP);
+               WriteDOC(0, docptr, NOP);
+       }
+
+       for (i = 0; i < 6; i++) {
+               ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+               if (ecc_code[i] != empty_write_ecc[i])
+                       emptymatch = 0;
+       }
+       WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+#if 0
+       /* If emptymatch=1, we might have an all-0xff data buffer.  Check. */
+       if (emptymatch) {
+               /* Note: this somewhat expensive test should not be triggered
+                  often.  It could be optimized away by examining the data in
+                  the writebuf routine, and remembering the result. */
+               for (i = 0; i < 512; i++) {
+                       if (dat[i] == 0xff) continue;
+                       emptymatch = 0;
+                       break;
+               }
+       }
+       /* If emptymatch still =1, we do have an all-0xff data buffer.
+          Return all-0xff ecc value instead of the computed one, so
+          it'll look just like a freshly-erased page. */
+       if (emptymatch) memset(ecc_code, 0xff, 6);
+#endif
+       return 0;
+}
+
+static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+{
+       int i, ret = 0;
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       unsigned long docptr = doc->virtadr;
+       volatile u_char dummy;
+       int emptymatch = 1;
+       
+       /* flush the pipeline */
+       if (DoC_is_2000(doc)) {
+               dummy = ReadDOC(docptr, 2k_ECCStatus);
+               dummy = ReadDOC(docptr, 2k_ECCStatus);
+               dummy = ReadDOC(docptr, 2k_ECCStatus);
+       } else {
+               dummy = ReadDOC(docptr, ECCConf);
+               dummy = ReadDOC(docptr, ECCConf);
+               dummy = ReadDOC(docptr, ECCConf);
+       }
+       
+       /* Error occured ? */
+       if (dummy & 0x80) {
+               for (i = 0; i < 6; i++) {
+                       calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+                       if (calc_ecc[i] != empty_read_syndrome[i])
+                               emptymatch = 0;
+               }
+               /* If emptymatch=1, the read syndrome is consistent with an
+                  all-0xff data and stored ecc block.  Check the stored ecc. */
+               if (emptymatch) {
+                       for (i = 0; i < 6; i++) {
+                               if (read_ecc[i] == 0xff) continue;
+                               emptymatch = 0;
+                               break;
+                       }
+               }
+               /* If emptymatch still =1, check the data block. */
+               if (emptymatch) {
+               /* Note: this somewhat expensive test should not be triggered
+                  often.  It could be optimized away by examining the data in
+                  the readbuf routine, and remembering the result. */
+                       for (i = 0; i < 512; i++) {
+                               if (dat[i] == 0xff) continue;
+                               emptymatch = 0;
+                               break;
+                       }
+               }
+               /* If emptymatch still =1, this is almost certainly a freshly-
+                  erased block, in which case the ECC will not come out right.
+                  We'll suppress the error and tell the caller everything's
+                  OK.  Because it is. */
+               if (!emptymatch) ret = doc_decode_ecc (dat, calc_ecc);
+               if (ret > 0)
+                       printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
+       }       
+       WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+       if (no_ecc_failures && (ret == -1)) {
+               printk(KERN_ERR "suppressing ECC failure\n");
+               ret = 0;
+       }
+       return ret;
+}
+               
+//u_char mydatabuf[528];
+
+static struct nand_oobinfo doc200x_oobinfo = {
+        .useecc = MTD_NANDECC_AUTOPLACE,
+        .eccbytes = 6,
+        .eccpos = {0, 1, 2, 3, 4, 5},
+        .oobfree = { {8, 8} }
+};
+/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
+   On sucessful return, buf will contain a copy of the media header for
+   further processing.  id is the string to scan for, and will presumably be
+   either "ANAND" or "BNAND".  If findmirror=1, also look for the mirror media
+   header.  The page #s of the found media headers are placed in mh0_page and
+   mh1_page in the DOC private structure. */
+static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
+                                    const char *id, int findmirror)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       int offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
+       int ret, retlen;
+
+       end = min(end, mtd->size); // paranoia
+       for (offs = 0; offs < end; offs += mtd->erasesize) {
+               ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
+               if (retlen != mtd->oobblock) continue;
+               if (ret) {
+                       printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n",
+                               offs);
+               }
+               if (memcmp(buf, id, 6)) continue;
+               printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
+               if (doc->mh0_page == -1) {
+                       doc->mh0_page = offs >> this->page_shift;
+                       if (!findmirror) return 1;
+                       continue;
+               }
+               doc->mh1_page = offs >> this->page_shift;
+               return 2;
+       }
+       if (doc->mh0_page == -1) {
+               printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id);
+               return 0;
+       }
+       /* Only one mediaheader was found.  We want buf to contain a
+          mediaheader on return, so we'll have to re-read the one we found. */
+       offs = doc->mh0_page << this->page_shift;
+       ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
+       if (retlen != mtd->oobblock) {
+               /* Insanity.  Give up. */
+               printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
+               return 0;
+       }
+       return 1;
+}
+
+static inline int __init nftl_partscan(struct mtd_info *mtd,
+                               struct mtd_partition *parts)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       u_char *buf = this->data_buf;
+       struct NFTLMediaHeader *mh = (struct NFTLMediaHeader *) buf;
+       const int psize = 1 << this->page_shift;
+       int blocks, maxblocks;
+       int offs, numheaders;
+
+       if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) return 0;
+
+//#ifdef CONFIG_MTD_DEBUG_VERBOSE
+//     if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+       printk(KERN_INFO "    DataOrgID        = %s\n"
+                        "    NumEraseUnits    = %d\n"
+                        "    FirstPhysicalEUN = %d\n"
+                        "    FormattedSize    = %d\n"
+                        "    UnitSizeFactor   = %d\n",
+               mh->DataOrgID, mh->NumEraseUnits,
+               mh->FirstPhysicalEUN, mh->FormattedSize,
+               mh->UnitSizeFactor);
+//#endif
+
+       blocks = mtd->size >> this->phys_erase_shift;
+       maxblocks = min(32768, mtd->erasesize - psize);
+
+       if (mh->UnitSizeFactor == 0x00) {
+               /* Auto-determine UnitSizeFactor.  The constraints are:
+                  - There can be at most 32768 virtual blocks.
+                  - There can be at most (virtual block size - page size)
+                    virtual blocks (because MediaHeader+BBT must fit in 1).
+               */
+               mh->UnitSizeFactor = 0xff;
+               while (blocks > maxblocks) {
+                       blocks >>= 1;
+                       maxblocks = min(32768, (maxblocks << 1) + psize);
+                       mh->UnitSizeFactor--;
+               }
+               printk(KERN_WARNING "UnitSizeFactor=0x00 detected.  Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
+       }
+
+       /* NOTE: The lines below modify internal variables of the NAND and MTD
+          layers; variables with have already been configured by nand_scan.
+          Unfortunately, we didn't know before this point what these values
+          should be.  Thus, this code is somewhat dependant on the exact
+          implementation of the NAND layer.  */
+       if (mh->UnitSizeFactor != 0xff) {
+               this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
+               mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
+               printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize);
+               blocks = mtd->size >> this->bbt_erase_shift;
+               maxblocks = min(32768, mtd->erasesize - psize);
+       }
+
+       if (blocks > maxblocks) {
+               printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size.  Aborting.\n", mh->UnitSizeFactor);
+               return 0;
+       }
+
+       /* Skip past the media headers. */
+       offs = max(doc->mh0_page, doc->mh1_page);
+       offs <<= this->page_shift;
+       offs += mtd->erasesize;
+
+       //parts[0].name = " DiskOnChip Boot / Media Header partition";
+       //parts[0].offset = 0;
+       //parts[0].size = offs;
+
+       parts[0].name = " DiskOnChip BDTL partition";
+       parts[0].offset = offs;
+       parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
+
+       offs += parts[0].size;
+       if (offs < mtd->size) {
+               parts[1].name = " DiskOnChip Remainder partition";
+               parts[1].offset = offs;
+               parts[1].size = mtd->size - offs;
+               return 2;
+       }
+       return 1;
+}
+
+/* This is a stripped-down copy of the code in inftlmount.c */
+static inline int __init inftl_partscan(struct mtd_info *mtd,
+                                struct mtd_partition *parts)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       u_char *buf = this->data_buf;
+       struct INFTLMediaHeader *mh = (struct INFTLMediaHeader *) buf;
+       struct INFTLPartition *ip;
+       int numparts = 0;
+       int blocks;
+       int vshift, lastvunit = 0;
+       int i;
+       int end = mtd->size;
+
+       if (inftl_bbt_write)
+               end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
+
+       if (!find_media_headers(mtd, buf, "BNAND", 0)) return 0;
+       doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
+
+       mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
+       mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
+       mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
+       mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
+       mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
+       mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
+//#ifdef CONFIG_MTD_DEBUG_VERBOSE
+//     if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+       printk(KERN_INFO "    bootRecordID          = %s\n"
+                        "    NoOfBootImageBlocks   = %d\n"
+                        "    NoOfBinaryPartitions  = %d\n"
+                        "    NoOfBDTLPartitions    = %d\n"
+                        "    BlockMultiplerBits    = %d\n"
+                        "    FormatFlgs            = %d\n"
+                        "    OsakVersion           = 0x%x\n"
+                        "    PercentUsed           = %d\n",
+               mh->bootRecordID, mh->NoOfBootImageBlocks,
+               mh->NoOfBinaryPartitions,
+               mh->NoOfBDTLPartitions,
+               mh->BlockMultiplierBits, mh->FormatFlags,
+               mh->OsakVersion, mh->PercentUsed);
+//#endif
+
+       vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
+
+       blocks = mtd->size >> vshift;
+       if (blocks > 32768) {
+               printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size.  Aborting.\n", mh->BlockMultiplierBits);
+               return 0;
+       }
+
+       blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
+       if (inftl_bbt_write && (blocks > mtd->erasesize)) {
+               printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported.  FIX ME!\n");
+               return 0;
+       }
+
+       /* Scan the partitions */
+       for (i = 0; (i < 4); i++) {
+               ip = &(mh->Partitions[i]);
+               ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
+               ip->firstUnit = le32_to_cpu(ip->firstUnit);
+               ip->lastUnit = le32_to_cpu(ip->lastUnit);
+               ip->flags = le32_to_cpu(ip->flags);
+               ip->spareUnits = le32_to_cpu(ip->spareUnits);
+               ip->Reserved0 = le32_to_cpu(ip->Reserved0);
+
+//#ifdef CONFIG_MTD_DEBUG_VERBOSE
+//             if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+               printk(KERN_INFO        "    PARTITION[%d] ->\n"
+                       "        virtualUnits    = %d\n"
+                       "        firstUnit       = %d\n"
+                       "        lastUnit        = %d\n"
+                       "        flags           = 0x%x\n"
+                       "        spareUnits      = %d\n",
+                       i, ip->virtualUnits, ip->firstUnit,
+                       ip->lastUnit, ip->flags,
+                       ip->spareUnits);
+//#endif
+
+/*
+               if ((i == 0) && (ip->firstUnit > 0)) {
+                       parts[0].name = " DiskOnChip IPL / Media Header partition";
+                       parts[0].offset = 0;
+                       parts[0].size = mtd->erasesize * ip->firstUnit;
+                       numparts = 1;
+               }
+*/
+
+               if (ip->flags & INFTL_BINARY)
+                       parts[numparts].name = " DiskOnChip BDK partition";
+               else
+                       parts[numparts].name = " DiskOnChip BDTL partition";
+               parts[numparts].offset = ip->firstUnit << vshift;
+               parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
+               numparts++;
+               if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit;
+               if (ip->flags & INFTL_LAST) break;
+       }
+       lastvunit++;
+       if ((lastvunit << vshift) < end) {
+               parts[numparts].name = " DiskOnChip Remainder partition";
+               parts[numparts].offset = lastvunit << vshift;
+               parts[numparts].size = end - parts[numparts].offset;
+               numparts++;
+       }
+       return numparts;
+}
+
+static int __init nftl_scan_bbt(struct mtd_info *mtd)
+{
+       int ret, numparts;
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       struct mtd_partition parts[2];
+
+       memset((char *) parts, 0, sizeof(parts));
+       /* On NFTL, we have to find the media headers before we can read the
+          BBTs, since they're stored in the media header eraseblocks. */
+       numparts = nftl_partscan(mtd, parts);
+       if (!numparts) return -EIO;
+       this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+                               NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+                               NAND_BBT_VERSION;
+       this->bbt_td->veroffs = 7;
+       this->bbt_td->pages[0] = doc->mh0_page + 1;
+       if (doc->mh1_page != -1) {
+               this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+                                       NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+                                       NAND_BBT_VERSION;
+               this->bbt_md->veroffs = 7;
+               this->bbt_md->pages[0] = doc->mh1_page + 1;
+       } else {
+               this->bbt_md = NULL;
+       }
+
+       /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
+          At least as nand_bbt.c is currently written. */
+       if ((ret = nand_scan_bbt(mtd, NULL)))
+               return ret;
+       add_mtd_device(mtd);
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+       if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
+#endif
+       return 0;
+}
+
+static int __init inftl_scan_bbt(struct mtd_info *mtd)
+{
+       int ret, numparts;
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+       struct mtd_partition parts[5];
+
+       if (this->numchips > doc->chips_per_floor) {
+               printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n");
+               return -EIO;
+       }
+
+       if (mtd->size == (8<<20)) {
+#if 0
+/* This doesn't seem to work for me.  I get ECC errors on every page. */
+               /* The Millennium 8MiB is actually an NFTL device! */
+               mtd->name = "DiskOnChip Millennium 8MiB (NFTL)";
+               return nftl_scan_bbt(mtd);
+#endif
+               printk(KERN_ERR "DiskOnChip Millennium 8MiB is not supported.\n");
+               return -EIO;
+       }
+
+       this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+                               NAND_BBT_VERSION;
+       if (inftl_bbt_write)
+               this->bbt_td->options |= NAND_BBT_WRITE;
+       this->bbt_td->offs = 8;
+       this->bbt_td->len = 8;
+       this->bbt_td->veroffs = 7;
+       this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+       this->bbt_td->reserved_block_code = 0x01;
+       this->bbt_td->pattern = "MSYS_BBT";
+
+       this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+                               NAND_BBT_VERSION;
+       if (inftl_bbt_write)
+               this->bbt_md->options |= NAND_BBT_WRITE;
+       this->bbt_md->offs = 8;
+       this->bbt_md->len = 8;
+       this->bbt_md->veroffs = 7;
+       this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+       this->bbt_md->reserved_block_code = 0x01;
+       this->bbt_md->pattern = "TBB_SYSM";
+
+       /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
+          At least as nand_bbt.c is currently written. */
+       if ((ret = nand_scan_bbt(mtd, NULL)))
+               return ret;
+       memset((char *) parts, 0, sizeof(parts));
+       numparts = inftl_partscan(mtd, parts);
+       /* At least for now, require the INFTL Media Header.  We could probably
+          do without it for non-INFTL use, since all it gives us is
+          autopartitioning, but I want to give it more thought. */
+       if (!numparts) return -EIO;
+       add_mtd_device(mtd);
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+       if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
+#endif
+       return 0;
+}
+
+static inline int __init doc2000_init(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+
+       this->write_byte = doc2000_write_byte;
+       this->read_byte = doc2000_read_byte;
+       this->write_buf = doc2000_writebuf;
+       this->read_buf = doc2000_readbuf;
+       this->verify_buf = doc2000_verifybuf;
+       this->scan_bbt = nftl_scan_bbt;
+
+       doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
+       doc2000_count_chips(mtd);
+       mtd->name = "DiskOnChip 2000 (NFTL Model)";
+       return (4 * doc->chips_per_floor);
+}
+
+static inline int __init doc2001_init(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       struct doc_priv *doc = (void *)this->priv;
+
+       this->write_byte = doc2001_write_byte;
+       this->read_byte = doc2001_read_byte;
+       this->write_buf = doc2001_writebuf;
+       this->read_buf = doc2001_readbuf;
+       this->verify_buf = doc2001_verifybuf;
+       this->scan_bbt = inftl_scan_bbt;
+
+       ReadDOC(doc->virtadr, ChipID);
+       ReadDOC(doc->virtadr, ChipID);
+       ReadDOC(doc->virtadr, ChipID);
+       if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) {
+               /* It's not a Millennium; it's one of the newer
+                  DiskOnChip 2000 units with a similar ASIC. 
+                  Treat it like a Millennium, except that it
+                  can have multiple chips. */
+               doc2000_count_chips(mtd);
+               mtd->name = "DiskOnChip 2000 (INFTL Model)";
+               return (4 * doc->chips_per_floor);
+       } else {
+               /* Bog-standard Millennium */
+               doc->chips_per_floor = 1;
+               mtd->name = "DiskOnChip Millennium";
+               return 1;
+       }
+}
+
+static inline int __init doc_probe(unsigned long physadr)
+{
+       unsigned char ChipID;
+       struct mtd_info *mtd;
+       struct nand_chip *nand;
+       struct doc_priv *doc;
+       unsigned long virtadr;
+       unsigned char save_control;
+       unsigned char tmp, tmpb, tmpc;
+       int reg, len, numchips;
+       int ret = 0;
+
+       virtadr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN);
+       if (!virtadr) {
+               printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
+               return -EIO;
+       }
+
+       /* It's not possible to cleanly detect the DiskOnChip - the
+        * bootup procedure will put the device into reset mode, and
+        * it's not possible to talk to it without actually writing
+        * to the DOCControl register. So we store the current contents
+        * of the DOCControl register's location, in case we later decide
+        * that it's not a DiskOnChip, and want to put it back how we
+        * found it. 
+        */
+       save_control = ReadDOC(virtadr, DOCControl);
+
+       /* Reset the DiskOnChip ASIC */
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
+                virtadr, DOCControl);
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
+                virtadr, DOCControl);
+
+       /* Enable the DiskOnChip ASIC */
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, 
+                virtadr, DOCControl);
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, 
+                virtadr, DOCControl);
+
+       ChipID = ReadDOC(virtadr, ChipID);
+
+       switch(ChipID) {
+       case DOC_ChipID_Doc2k:
+               reg = DoC_2k_ECCStatus;
+               break;
+       case DOC_ChipID_DocMil:
+               reg = DoC_ECCConf;
+               break;
+       default:
+               ret = -ENODEV;
+               goto notfound;
+       }
+       /* Check the TOGGLE bit in the ECC register */
+       tmp  = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+       tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+       tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+       if ((tmp == tmpb) || (tmp != tmpc)) {
+               printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
+               ret = -ENODEV;
+               goto notfound;
+       }
+
+       for (mtd = doclist; mtd; mtd = doc->nextdoc) {
+               nand = mtd->priv;
+               doc = (void *)nand->priv;
+               /* Use the alias resolution register to determine if this is
+                  in fact the same DOC aliased to a new address.  If writes
+                  to one chip's alias resolution register change the value on
+                  the other chip, they're the same chip. */
+               unsigned char oldval = ReadDOC(doc->virtadr, AliasResolution);
+               unsigned char newval = ReadDOC(virtadr, AliasResolution);
+               if (oldval != newval)
+                       continue;
+               WriteDOC(~newval, virtadr, AliasResolution);
+               oldval = ReadDOC(doc->virtadr, AliasResolution);
+               WriteDOC(newval, virtadr, AliasResolution); // restore it
+               newval = ~newval;
+               if (oldval == newval) {
+                       //printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
+                       goto notfound;
+               }
+       }
+
+       printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
+
+       len = sizeof(struct mtd_info) +
+             sizeof(struct nand_chip) +
+             sizeof(struct doc_priv) +
+             (2 * sizeof(struct nand_bbt_descr));
+       mtd = kmalloc(len, GFP_KERNEL);
+       if (!mtd) {
+               printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
+               ret = -ENOMEM;
+               goto fail;
+       }
+       memset(mtd, 0, len);
+
+       nand                    = (struct nand_chip *) (mtd + 1);
+       doc                     = (struct doc_priv *) (nand + 1);
+       nand->bbt_td            = (struct nand_bbt_descr *) (doc + 1);
+       nand->bbt_md            = nand->bbt_td + 1;
+
+       mtd->priv               = (void *) nand;
+       mtd->owner              = THIS_MODULE;
+
+       nand->priv              = (void *) doc;
+       nand->select_chip       = doc200x_select_chip;
+       nand->hwcontrol         = doc200x_hwcontrol;
+       nand->dev_ready         = doc200x_dev_ready;
+       nand->waitfunc          = doc200x_wait;
+       nand->block_bad         = doc200x_block_bad;
+       nand->enable_hwecc      = doc200x_enable_hwecc;
+       nand->calculate_ecc     = doc200x_calculate_ecc;
+       nand->correct_data      = doc200x_correct_data;
+       //nand->data_buf
+       nand->autooob           = &doc200x_oobinfo;
+       nand->eccmode           = NAND_ECC_HW6_512;
+       nand->options           = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
+
+       doc->physadr            = physadr;
+       doc->virtadr            = virtadr;
+       doc->ChipID             = ChipID;
+       doc->curfloor           = -1;
+       doc->curchip            = -1;
+       doc->mh0_page           = -1;
+       doc->mh1_page           = -1;
+       doc->nextdoc            = doclist;
+
+       if (ChipID == DOC_ChipID_Doc2k)
+               numchips = doc2000_init(mtd);
+       else
+               numchips = doc2001_init(mtd);
+
+       if ((ret = nand_scan(mtd, numchips))) {
+               /* DBB note: i believe nand_release is necessary here, as
+                  buffers may have been allocated in nand_base.  Check with
+                  Thomas. FIX ME! */
+               /* nand_release will call del_mtd_device, but we haven't yet
+                  added it.  This is handled without incident by
+                  del_mtd_device, as far as I can tell. */
+               nand_release(mtd);
+               kfree(mtd);
+               goto fail;
+       }
+
+       /* Success! */
+       doclist = mtd;
+       return 0;
+
+notfound:
+       /* Put back the contents of the DOCControl register, in case it's not
+          actually a DiskOnChip.  */
+       WriteDOC(save_control, virtadr, DOCControl);
+fail:
+       iounmap((void *)virtadr);
+       return ret;
+}
+
+int __init init_nanddoc(void)
+{
+       int i;
+
+       if (doc_config_location) {
+               printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
+               return doc_probe(doc_config_location);
+       } else {
+               for (i=0; (doc_locations[i] != 0xffffffff); i++) {
+                       doc_probe(doc_locations[i]);
+               }
+       }
+       /* No banner message any more. Print a message if no DiskOnChip
+          found, so the user knows we at least tried. */
+       if (!doclist) {
+               printk(KERN_INFO "No valid DiskOnChip devices found\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+void __exit cleanup_nanddoc(void)
+{
+       struct mtd_info *mtd, *nextmtd;
+       struct nand_chip *nand;
+       struct doc_priv *doc;
+
+       for (mtd = doclist; mtd; mtd = nextmtd) {
+               nand = mtd->priv;
+               doc = (void *)nand->priv;
+
+               nextmtd = doc->nextdoc;
+               nand_release(mtd);
+               iounmap((void *)doc->virtadr);
+               kfree(mtd);
+       }
+}
+       
+module_init(init_nanddoc);
+module_exit(cleanup_nanddoc);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n");
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
new file mode 100644 (file)
index 0000000..596bc8f
--- /dev/null
@@ -0,0 +1,2581 @@
+/*
+ *  drivers/mtd/nand.c
+ *
+ *  Overview:
+ *   This is the generic MTD driver for NAND flash devices. It should be
+ *   capable of working with almost all NAND chips currently available.
+ *   Basic support for AG-AND chips is provided.
+ *   
+ *     Additional technical information is available on
+ *     http://www.linux-mtd.infradead.org/tech/nand.html
+ *     
+ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *               2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ *  02-08-2004  tglx: support for strange chips, which cannot auto increment 
+ *             pages on read / read_oob
+ *
+ *  03-17-2004  tglx: Check ready before auto increment check. Simon Bayes
+ *             pointed this out, as he marked an auto increment capable chip
+ *             as NOAUTOINCR in the board driver.
+ *             Make reads over block boundaries work too
+ *
+ *  04-14-2004 tglx: first working version for 2k page size chips
+ *  
+ *  05-19-2004  tglx: Basic support for Renesas AG-AND chips
+ *
+ * Credits:
+ *     David Woodhouse for adding multichip support  
+ *     
+ *     Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+ *     rework for 2K page size chips
+ *
+ * TODO:
+ *     Enable cached programming for 2k page size chips
+ *     Check, if mtd->ecctype should be set to MTD_ECC_HW
+ *     if we have HW ecc support.
+ *     The AG-AND chips have nice features for speed improvement,
+ *     which are not supported yet. Read / program 4 pages in one go.
+ *
+ * $Id: nand_base.c,v 1.113 2004/07/14 16:31:31 gleixner Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+#include <linux/mtd/partitions.h>
+#endif
+
+/* Define default oob placement schemes for large and small page devices */
+static struct nand_oobinfo nand_oob_8 = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 3,
+       .eccpos = {0, 1, 2},
+       .oobfree = { {3, 2}, {6, 2} }
+};
+
+static struct nand_oobinfo nand_oob_16 = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 6,
+       .eccpos = {0, 1, 2, 3, 6, 7},
+       .oobfree = { {8, 8} }
+};
+
+static struct nand_oobinfo nand_oob_64 = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 24,
+       .eccpos = {
+               40, 41, 42, 43, 44, 45, 46, 47, 
+               48, 49, 50, 51, 52, 53, 54, 55, 
+               56, 57, 58, 59, 60, 61, 62, 63},
+       .oobfree = { {2, 38} }
+};
+
+/* This is used for padding purposes in nand_write_oob */
+static u_char ffchars[] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+/*
+ * NAND low-level MTD interface functions
+ */
+static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
+static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
+static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
+
+static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+                         size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+                          size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
+static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
+                       unsigned long count, loff_t to, size_t * retlen);
+static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs,
+                       unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
+static void nand_sync (struct mtd_info *mtd);
+
+/* Some internal functions */
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,
+               struct nand_oobinfo *oobsel, int mode);
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, 
+       u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode);
+#else
+#define nand_verify_pages(...) (0)
+#endif
+               
+static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state);
+
+/**
+ * nand_release_chip - [GENERIC] release chip
+ * @mtd:       MTD device structure
+ * 
+ * Deselect, release chip lock and wake up anyone waiting on the device 
+ */
+static void nand_release_chip (struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+
+       /* De-select the NAND device */
+       this->select_chip(mtd, -1);
+       /* Release the chip */
+       spin_lock_bh (&this->chip_lock);
+       this->state = FL_READY;
+       wake_up (&this->wq);
+       spin_unlock_bh (&this->chip_lock);
+}
+
+/**
+ * nand_read_byte - [DEFAULT] read one byte from the chip
+ * @mtd:       MTD device structure
+ *
+ * Default read function for 8bit buswith
+ */
+static u_char nand_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       return readb(this->IO_ADDR_R);
+}
+
+/**
+ * nand_write_byte - [DEFAULT] write one byte to the chip
+ * @mtd:       MTD device structure
+ * @byte:      pointer to data byte to write
+ *
+ * Default write function for 8it buswith
+ */
+static void nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+       struct nand_chip *this = mtd->priv;
+       writeb(byte, this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
+ * @mtd:       MTD device structure
+ *
+ * Default read function for 16bit buswith with 
+ * endianess conversion
+ */
+static u_char nand_read_byte16(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
+}
+
+/**
+ * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip
+ * @mtd:       MTD device structure
+ * @byte:      pointer to data byte to write
+ *
+ * Default write function for 16bit buswith with
+ * endianess conversion
+ */
+static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
+{
+       struct nand_chip *this = mtd->priv;
+       writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_word - [DEFAULT] read one word from the chip
+ * @mtd:       MTD device structure
+ *
+ * Default read function for 16bit buswith without 
+ * endianess conversion
+ */
+static u16 nand_read_word(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       return readw(this->IO_ADDR_R);
+}
+
+/**
+ * nand_write_word - [DEFAULT] write one word to the chip
+ * @mtd:       MTD device structure
+ * @word:      data word to write
+ *
+ * Default write function for 16bit buswith without 
+ * endianess conversion
+ */
+static void nand_write_word(struct mtd_info *mtd, u16 word)
+{
+       struct nand_chip *this = mtd->priv;
+       writew(word, this->IO_ADDR_W);
+}
+
+/**
+ * nand_select_chip - [DEFAULT] control CE line
+ * @mtd:       MTD device structure
+ * @chip:      chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void nand_select_chip(struct mtd_info *mtd, int chip)
+{
+       struct nand_chip *this = mtd->priv;
+       switch(chip) {
+       case -1:
+               this->hwcontrol(mtd, NAND_CTL_CLRNCE);  
+               break;
+       case 0:
+               this->hwcontrol(mtd, NAND_CTL_SETNCE);
+               break;
+
+       default:
+               BUG();
+       }
+}
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @mtd:       MTD device structure
+ * @buf:       data buffer
+ * @len:       number of bytes to write
+ *
+ * Default write function for 8bit buswith
+ */
+static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+
+       for (i=0; i<len; i++)
+               writeb(buf[i], this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_buf - [DEFAULT] read chip data into buffer 
+ * @mtd:       MTD device structure
+ * @buf:       buffer to store date
+ * @len:       number of bytes to read
+ *
+ * Default read function for 8bit buswith
+ */
+static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+
+       for (i=0; i<len; i++)
+               buf[i] = readb(this->IO_ADDR_R);
+}
+
+/**
+ * nand_verify_buf - [DEFAULT] Verify chip data against buffer 
+ * @mtd:       MTD device structure
+ * @buf:       buffer containing the data to compare
+ * @len:       number of bytes to compare
+ *
+ * Default verify function for 8bit buswith
+ */
+static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+
+       for (i=0; i<len; i++)
+               if (buf[i] != readb(this->IO_ADDR_R))
+                       return -EFAULT;
+
+       return 0;
+}
+
+/**
+ * nand_write_buf16 - [DEFAULT] write buffer to chip
+ * @mtd:       MTD device structure
+ * @buf:       data buffer
+ * @len:       number of bytes to write
+ *
+ * Default write function for 16bit buswith
+ */
+static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+       u16 *p = (u16 *) buf;
+       len >>= 1;
+       
+       for (i=0; i<len; i++)
+               writew(p[i], this->IO_ADDR_W);
+               
+}
+
+/**
+ * nand_read_buf16 - [DEFAULT] read chip data into buffer 
+ * @mtd:       MTD device structure
+ * @buf:       buffer to store date
+ * @len:       number of bytes to read
+ *
+ * Default read function for 16bit buswith
+ */
+static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+       u16 *p = (u16 *) buf;
+       len >>= 1;
+
+       for (i=0; i<len; i++)
+               p[i] = readw(this->IO_ADDR_R);
+}
+
+/**
+ * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer 
+ * @mtd:       MTD device structure
+ * @buf:       buffer containing the data to compare
+ * @len:       number of bytes to compare
+ *
+ * Default verify function for 16bit buswith
+ */
+static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+       u16 *p = (u16 *) buf;
+       len >>= 1;
+
+       for (i=0; i<len; i++)
+               if (p[i] != readw(this->IO_ADDR_R))
+                       return -EFAULT;
+
+       return 0;
+}
+
+/**
+ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+ * @mtd:       MTD device structure
+ * @ofs:       offset from device start
+ * @getchip:   0, if the chip is already selected
+ *
+ * Check, if the block is bad. 
+ */
+static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+       int page, chipnr, res = 0;
+       struct nand_chip *this = mtd->priv;
+       u16 bad;
+
+       if (getchip) {
+               page = (int)(ofs >> this->page_shift);
+               chipnr = (int)(ofs >> this->chip_shift);
+
+               /* Grab the lock and see if the device is available */
+               nand_get_chip (this, mtd, FL_READING);
+
+               /* Select the NAND device */
+               this->select_chip(mtd, chipnr);
+       } else 
+               page = (int) ofs;       
+
+       if (this->options & NAND_BUSWIDTH_16) {
+               this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);
+               bad = cpu_to_le16(this->read_word(mtd));
+               if (this->badblockpos & 0x1)
+                       bad >>= 1;
+               if ((bad & 0xFF) != 0xff)
+                       res = 1;
+       } else {
+               this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);
+               if (this->read_byte(mtd) != 0xff)
+                       res = 1;
+       }
+               
+       if (getchip) {
+               /* Deselect and wake up anyone waiting on the device */
+               nand_release_chip(mtd);
+       }       
+       
+       return res;
+}
+
+/**
+ * nand_default_block_markbad - [DEFAULT] mark a block bad
+ * @mtd:       MTD device structure
+ * @ofs:       offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+*/
+static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+       struct nand_chip *this = mtd->priv;
+       u_char buf[2] = {0, 0};
+       size_t  retlen;
+       int block;
+       
+       /* Get block number */
+       block = ((int) ofs) >> this->bbt_erase_shift;
+       this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+       /* Do we have a flash based bad block table ? */
+       if (this->options & NAND_USE_FLASH_BBT)
+               return nand_update_bbt (mtd, ofs);
+               
+       /* We write two bytes, so we dont have to mess with 16 bit access */
+       ofs += mtd->oobsize + (this->badblockpos & ~0x01);
+       return nand_write_oob (mtd, ofs , 2, &retlen, buf);
+}
+
+/** 
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @mtd:       MTD device structure
+ * Check, if the device is write protected 
+ *
+ * The function expects, that the device is already selected 
+ */
+static int nand_check_wp (struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       /* Check the WP bit */
+       this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+       return (this->read_byte(mtd) & 0x80) ? 0 : 1; 
+}
+
+/**
+ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd:       MTD device structure
+ * @ofs:       offset from device start
+ * @getchip:   0, if the chip is already selected
+ * @allowbbt:  1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+{
+       struct nand_chip *this = mtd->priv;
+       
+       if (!this->bbt)
+               return this->block_bad(mtd, ofs, getchip);
+       
+       /* Return info from the table */
+       return nand_isbad_bbt (mtd, ofs, allowbbt);
+}
+
+/**
+ * nand_command - [DEFAULT] Send command to NAND device
+ * @mtd:       MTD device structure
+ * @command:   the command to be sent
+ * @column:    the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This function is used for small page
+ * devices (256/512 Bytes per page)
+ */
+static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+       register struct nand_chip *this = mtd->priv;
+
+       /* Begin command latch cycle */
+       this->hwcontrol(mtd, NAND_CTL_SETCLE);
+       /*
+        * Write out the command to the device.
+        */
+       if (command == NAND_CMD_SEQIN) {
+               int readcmd;
+
+               if (column >= mtd->oobblock) {
+                       /* OOB area */
+                       column -= mtd->oobblock;
+                       readcmd = NAND_CMD_READOOB;
+               } else if (column < 256) {
+                       /* First 256 bytes --> READ0 */
+                       readcmd = NAND_CMD_READ0;
+               } else {
+                       column -= 256;
+                       readcmd = NAND_CMD_READ1;
+               }
+               this->write_byte(mtd, readcmd);
+       }
+       this->write_byte(mtd, command);
+
+       /* Set ALE and clear CLE to start address cycle */
+       this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+       if (column != -1 || page_addr != -1) {
+               this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+               /* Serially input address */
+               if (column != -1) {
+                       /* Adjust columns for 16 bit buswidth */
+                       if (this->options & NAND_BUSWIDTH_16)
+                               column >>= 1;
+                       this->write_byte(mtd, column);
+               }
+               if (page_addr != -1) {
+                       this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+                       this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+                       /* One more address cycle for higher density devices */
+                       if (this->chipsize & 0x0c000000) 
+                               this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
+               }
+               /* Latch in address */
+               this->hwcontrol(mtd, NAND_CTL_CLRALE);
+       }
+       
+       /* 
+        * program and erase have their own busy handlers 
+        * status and sequential in needs no delay
+       */
+       switch (command) {
+                       
+       case NAND_CMD_PAGEPROG:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_SEQIN:
+       case NAND_CMD_STATUS:
+               return;
+
+       case NAND_CMD_RESET:
+               if (this->dev_ready)    
+                       break;
+               udelay(this->chip_delay);
+               this->hwcontrol(mtd, NAND_CTL_SETCLE);
+               this->write_byte(mtd, NAND_CMD_STATUS);
+               this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+               while ( !(this->read_byte(mtd) & 0x40));
+               return;
+
+       /* This applies to read commands */     
+       default:
+               /* 
+                * If we don't have access to the busy pin, we apply the given
+                * command delay
+               */
+               if (!this->dev_ready) {
+                       udelay (this->chip_delay);
+                       return;
+               }       
+       }
+       
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay (100);
+       /* wait until command is processed */
+       while (!this->dev_ready(mtd));
+}
+
+/**
+ * nand_command_lp - [DEFAULT] Send command to NAND large page device
+ * @mtd:       MTD device structure
+ * @command:   the command to be sent
+ * @column:    the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This is the version for the new large page devices
+ * We dont have the seperate regions as we have in the small page devices.
+ * We must emulate NAND_CMD_READOOB to keep the code compatible.
+ *
+ */
+static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+       register struct nand_chip *this = mtd->priv;
+
+       /* Emulate NAND_CMD_READOOB */
+       if (command == NAND_CMD_READOOB) {
+               column += mtd->oobblock;
+               command = NAND_CMD_READ0;
+       }
+       
+               
+       /* Begin command latch cycle */
+       this->hwcontrol(mtd, NAND_CTL_SETCLE);
+       /* Write out the command to the device. */
+       this->write_byte(mtd, command);
+       /* End command latch cycle */
+       this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+       if (column != -1 || page_addr != -1) {
+               this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+               /* Serially input address */
+               if (column != -1) {
+                       /* Adjust columns for 16 bit buswidth */
+                       if (this->options & NAND_BUSWIDTH_16)
+                               column >>= 1;
+                       this->write_byte(mtd, column & 0xff);
+                       this->write_byte(mtd, column >> 8);
+               }       
+               if (page_addr != -1) {
+                       this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+                       this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+                       /* One more address cycle for devices > 128MiB */
+                       if (this->chipsize > (128 << 20))
+                               this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
+               }
+               /* Latch in address */
+               this->hwcontrol(mtd, NAND_CTL_CLRALE);
+       }
+       
+       /* 
+        * program and erase have their own busy handlers 
+        * status and sequential in needs no delay
+       */
+       switch (command) {
+                       
+       case NAND_CMD_CACHEDPROG:
+       case NAND_CMD_PAGEPROG:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_SEQIN:
+       case NAND_CMD_STATUS:
+               return;
+
+
+       case NAND_CMD_RESET:
+               if (this->dev_ready)    
+                       break;
+               udelay(this->chip_delay);
+               this->hwcontrol(mtd, NAND_CTL_SETCLE);
+               this->write_byte(mtd, NAND_CMD_STATUS);
+               this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+               while ( !(this->read_byte(mtd) & 0x40));
+               return;
+
+       case NAND_CMD_READ0:
+               /* Begin command latch cycle */
+               this->hwcontrol(mtd, NAND_CTL_SETCLE);
+               /* Write out the start read command */
+               this->write_byte(mtd, NAND_CMD_READSTART);
+               /* End command latch cycle */
+               this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+               /* Fall through into ready check */
+               
+       /* This applies to read commands */     
+       default:
+               /* 
+                * If we don't have access to the busy pin, we apply the given
+                * command delay
+               */
+               if (!this->dev_ready) {
+                       udelay (this->chip_delay);
+                       return;
+               }       
+       }
+       
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay (100);
+       /* wait until command is processed */
+       while (!this->dev_ready(mtd));
+}
+
+/**
+ * nand_get_chip - [GENERIC] Get chip for selected access
+ * @this:      the nand chip descriptor
+ * @mtd:       MTD device structure
+ * @new_state: the state which is requested 
+ *
+ * Get the device and lock it for exclusive access
+ */
+static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+{
+
+       DECLARE_WAITQUEUE (wait, current);
+
+       /* 
+        * Grab the lock and see if the device is available 
+       */
+retry:
+       spin_lock_bh (&this->chip_lock);
+
+       if (this->state == FL_READY) {
+               this->state = new_state;
+               spin_unlock_bh (&this->chip_lock);
+               return;
+       }
+
+       set_current_state (TASK_UNINTERRUPTIBLE);
+       add_wait_queue (&this->wq, &wait);
+       spin_unlock_bh (&this->chip_lock);
+       schedule ();
+       remove_wait_queue (&this->wq, &wait);
+       goto retry;
+}
+
+/**
+ * nand_wait - [DEFAULT]  wait until the command is done
+ * @mtd:       MTD device structure
+ * @this:      NAND chip structure
+ * @state:     state to select the max. timeout value
+ *
+ * Wait for command done. This applies to erase and program only
+ * Erase can take up to 400ms and program up to 20ms according to 
+ * general NAND and SmartMedia specs
+ *
+*/
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+{
+
+       unsigned long   timeo = jiffies;
+       int     status;
+       
+       if (state == FL_ERASING)
+                timeo += (HZ * 400) / 1000;
+       else
+                timeo += (HZ * 20) / 1000;
+
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay (100);
+
+       spin_lock_bh (&this->chip_lock);
+       if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
+               this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
+       else    
+               this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+
+       while (time_before(jiffies, timeo)) {           
+               /* Check, if we were interrupted */
+               if (this->state != state) {
+                       spin_unlock_bh (&this->chip_lock);
+                       return 0;
+               }
+               if (this->dev_ready) {
+                       if (this->dev_ready(mtd))
+                               break;
+               }
+               if (this->read_byte(mtd) & NAND_STATUS_READY)
+                       break;
+                                               
+               spin_unlock_bh (&this->chip_lock);
+               yield ();
+               spin_lock_bh (&this->chip_lock);
+       }
+       status = (int) this->read_byte(mtd);
+       spin_unlock_bh (&this->chip_lock);
+
+       return status;
+}
+
+/**
+ * nand_write_page - [GENERIC] write one page
+ * @mtd:       MTD device structure
+ * @this:      NAND chip structure
+ * @page:      startpage inside the chip, must be called with (page & this->pagemask)
+ * @oob_buf:   out of band data buffer
+ * @oobsel:    out of band selecttion structre
+ * @cached:    1 = enable cached programming if supported by chip
+ *
+ * Nand_page_program function is used for write and writev !
+ * This function will always program a full page of data
+ * If you call it with a non page aligned buffer, you're lost :)
+ *
+ * Cached programming is not supported yet.
+ */
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, 
+       u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached)
+{
+       int     i, status;
+       u_char  ecc_code[8];
+       int     eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
+       int     *oob_config = oobsel->eccpos;
+       int     datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
+       int     eccbytes = 0;
+       
+       /* FIXME: Enable cached programming */
+       cached = 0;
+       
+       /* Send command to begin auto page programming */
+       this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
+
+       /* Write out complete page of data, take care of eccmode */
+       switch (eccmode) {
+       /* No ecc, write all */
+       case NAND_ECC_NONE:
+               printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
+               this->write_buf(mtd, this->data_poi, mtd->oobblock);
+               break;
+               
+       /* Software ecc 3/256, write all */
+       case NAND_ECC_SOFT:
+               for (; eccsteps; eccsteps--) {
+                       this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
+                       for (i = 0; i < 3; i++, eccidx++)
+                               oob_buf[oob_config[eccidx]] = ecc_code[i];
+                       datidx += this->eccsize;
+               }
+               this->write_buf(mtd, this->data_poi, mtd->oobblock);
+               break;
+               
+       /* Hardware ecc 8 byte / 512 byte data */       
+       case NAND_ECC_HW8_512:  
+               eccbytes += 2;
+       /* Hardware ecc 6 byte / 512 byte data */       
+       case NAND_ECC_HW6_512:  
+               eccbytes += 3;
+       /* Hardware ecc 3 byte / 256 data */    
+       /* Hardware ecc 3 byte / 512 byte data */       
+       case NAND_ECC_HW3_256:          
+       case NAND_ECC_HW3_512:
+               eccbytes += 3;
+               for (; eccsteps; eccsteps--) {
+                       /* enable hardware ecc logic for write */
+                       this->enable_hwecc(mtd, NAND_ECC_WRITE);
+                       this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
+                       this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
+                       for (i = 0; i < eccbytes; i++, eccidx++)
+                               oob_buf[oob_config[eccidx]] = ecc_code[i];
+                       /* If the hardware ecc provides syndromes then
+                        * the ecc code must be written immidiately after
+                        * the data bytes (words) */
+                       if (this->options & NAND_HWECC_SYNDROME)
+                               this->write_buf(mtd, ecc_code, eccbytes);
+
+                       datidx += this->eccsize;
+               }
+               break;
+
+       default:
+               printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+               BUG();  
+       }
+                                                                               
+       /* Write out OOB data */
+       if (this->options & NAND_HWECC_SYNDROME)
+               this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
+       else 
+               this->write_buf(mtd, oob_buf, mtd->oobsize);
+
+       /* Send command to actually program the data */
+       this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
+
+       if (!cached) {
+               /* call wait ready function */
+               status = this->waitfunc (mtd, this, FL_WRITING);
+               /* See if device thinks it succeeded */
+               if (status & 0x01) {
+                       DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
+                       return -EIO;
+               }
+       } else {
+               /* FIXME: Implement cached programming ! */
+               /* wait until cache is ready*/
+               // status = this->waitfunc (mtd, this, FL_CACHEDRPG);
+       }
+       return 0;       
+}
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+/**
+ * nand_verify_pages - [GENERIC] verify the chip contents after a write
+ * @mtd:       MTD device structure
+ * @this:      NAND chip structure
+ * @page:      startpage inside the chip, must be called with (page & this->pagemask)
+ * @numpages:  number of pages to verify
+ * @oob_buf:   out of band data buffer
+ * @oobsel:    out of band selecttion structre
+ * @chipnr:    number of the current chip
+ * @oobmode:   1 = full buffer verify, 0 = ecc only
+ *
+ * The NAND device assumes that it is always writing to a cleanly erased page.
+ * Hence, it performs its internal write verification only on bits that 
+ * transitioned from 1 to 0. The device does NOT verify the whole page on a
+ * byte by byte basis. It is possible that the page was not completely erased 
+ * or the page is becoming unusable due to wear. The read with ECC would catch 
+ * the error later when the ECC page check fails, but we would rather catch 
+ * it early in the page write stage. Better to write no data than invalid data.
+ */
+static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, 
+       u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
+{
+       int     i, j, datidx = 0, oobofs = 0, res = -EIO;
+       int     eccsteps = this->eccsteps;
+       int     hweccbytes; 
+       u_char  oobdata[64];
+
+       hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
+
+       /* Send command to read back the first page */
+       this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
+
+       for(;;) {
+               for (j = 0; j < eccsteps; j++) {
+                       /* Loop through and verify the data */
+                       if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+                               goto out;
+                       }
+                       datidx += mtd->eccsize;
+                       /* Have we a hw generator layout ? */
+                       if (!hweccbytes)
+                               continue;
+                       if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+                               goto out;
+                       }
+                       oobofs += hweccbytes;
+               }
+
+               /* check, if we must compare all data or if we just have to
+                * compare the ecc bytes
+                */
+               if (oobmode) {
+                       if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+                               goto out;
+                       }
+               } else {
+                       /* Read always, else autoincrement fails */
+                       this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
+
+                       if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
+                               int ecccnt = oobsel->eccbytes;
+               
+                               for (i = 0; i < ecccnt; i++) {
+                                       int idx = oobsel->eccpos[i];
+                                       if (oobdata[idx] != oob_buf[oobofs + idx] ) {
+                                               DEBUG (MTD_DEBUG_LEVEL0,
+                                               "%s: Failed ECC write "
+                                               "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
+                                               goto out;
+                                       }
+                               }
+                       }       
+               }
+               oobofs += mtd->oobsize - hweccbytes * eccsteps;
+               page++;
+               numpages--;
+
+               /* Apply delay or wait for ready/busy pin 
+                * Do this before the AUTOINCR check, so no problems
+                * arise if a chip which does auto increment
+                * is marked as NOAUTOINCR by the board driver.
+                * Do this also before returning, so the chip is
+                * ready for the next command.
+               */
+               if (!this->dev_ready) 
+                       udelay (this->chip_delay);
+               else
+                       while (!this->dev_ready(mtd));  
+
+               /* All done, return happy */
+               if (!numpages)
+                       return 0;
+               
+                       
+               /* Check, if the chip supports auto page increment */ 
+               if (!NAND_CANAUTOINCR(this))
+                       this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+       }
+       /* 
+        * Terminate the read command. We come here in case of an error
+        * So we must issue a reset command.
+        */
+out:    
+       this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1);
+       return res;
+}
+#endif
+
+/**
+ * nand_read - [MTD Interface] MTD compability function for nand_read_ecc
+ * @mtd:       MTD device structure
+ * @from:      offset to read from
+ * @len:       number of bytes to read
+ * @retlen:    pointer to variable to store the number of read bytes
+ * @buf:       the databuffer to put data
+ *
+ * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL
+*/
+static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+{
+       return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
+}                         
+
+
+/**
+ * nand_read_ecc - [MTD Interface] Read data with ECC
+ * @mtd:       MTD device structure
+ * @from:      offset to read from
+ * @len:       number of bytes to read
+ * @retlen:    pointer to variable to store the number of read bytes
+ * @buf:       the databuffer to put data
+ * @oob_buf:   filesystem supplied oob data buffer
+ * @oobsel:    oob selection structure
+ *
+ * NAND read with ECC
+ */
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+                         size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
+{
+       int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
+       int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
+       struct nand_chip *this = mtd->priv;
+       u_char *data_poi, *oob_data = oob_buf;
+       u_char ecc_calc[32];
+       u_char ecc_code[32];
+        int eccmode, eccsteps;
+       int     *oob_config, datidx;
+       int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+       int     eccbytes = 3;
+       int     compareecc = 1;
+       int     oobreadlen;
+
+
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+       /* Do not allow reads past end of device */
+       if ((from + len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
+               *retlen = 0;
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       nand_get_chip (this, mtd ,FL_READING);
+
+       /* use userspace supplied oobinfo, if zero */
+       if (oobsel == NULL)
+               oobsel = &mtd->oobinfo;
+       
+       /* Autoplace of oob data ? Use the default placement scheme */
+       if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
+               oobsel = this->autooob;
+               
+       eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
+       oob_config = oobsel->eccpos;
+
+       /* Select the NAND device */
+       chipnr = (int)(from >> this->chip_shift);
+       this->select_chip(mtd, chipnr);
+
+       /* First we calculate the starting page */
+       realpage = (int) (from >> this->page_shift);
+       page = realpage & this->pagemask;
+
+       /* Get raw starting column */
+       col = from & (mtd->oobblock - 1);
+
+       end = mtd->oobblock;
+       ecc = this->eccsize;
+       switch (eccmode) {
+       case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data  */
+               eccbytes = 6;
+               break;                                          
+       case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data  */
+               eccbytes = 8;
+               break;
+       case NAND_ECC_NONE:
+               compareecc = 0;
+               break;                                          
+       }        
+
+       if (this->options & NAND_HWECC_SYNDROME)
+               compareecc = 0;
+
+       oobreadlen = mtd->oobsize;
+       if (this->options & NAND_HWECC_SYNDROME) 
+               oobreadlen -= oobsel->eccbytes;
+
+       /* Loop until all data read */
+       while (read < len) {
+               
+               int aligned = (!col && (len - read) >= end);
+               /* 
+                * If the read is not page aligned, we have to read into data buffer
+                * due to ecc, else we read into return buffer direct
+                */
+               if (aligned)
+                       data_poi = &buf[read];
+               else 
+                       data_poi = this->data_buf;
+               
+               /* Check, if we have this page in the buffer 
+                *
+                * FIXME: Make it work when we must provide oob data too,
+                * check the usage of data_buf oob field
+                */
+               if (realpage == this->pagebuf && !oob_buf) {
+                       /* aligned read ? */
+                       if (aligned)
+                               memcpy (data_poi, this->data_buf, end);
+                       goto readdata;
+               }
+
+               /* Check, if we must send the read command */
+               if (sndcmd) {
+                       this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+                       sndcmd = 0;
+               }       
+
+               /* get oob area, if we have no oob buffer from fs-driver */
+               if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE)
+                       oob_data = &this->data_buf[end];
+
+               eccsteps = this->eccsteps;
+               
+               switch (eccmode) {
+               case NAND_ECC_NONE: {   /* No ECC, Read in a page */
+                       static unsigned long lastwhinge = 0;
+                       if ((lastwhinge / HZ) != (jiffies / HZ)) {
+                               printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
+                               lastwhinge = jiffies;
+                       }
+                       this->read_buf(mtd, data_poi, end);
+                       break;
+               }
+                       
+               case NAND_ECC_SOFT:     /* Software ECC 3/256: Read in a page + oob data */
+                       this->read_buf(mtd, data_poi, end);
+                       for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) 
+                               this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+                       break;  
+                       
+               case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */
+               case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */ 
+               case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data  */
+               case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data  */
+                       for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
+                               this->enable_hwecc(mtd, NAND_ECC_READ); 
+                               this->read_buf(mtd, &data_poi[datidx], ecc);
+
+                               /* HW ecc with syndrome calculation must read the
+                                * syndrome from flash immidiately after the data */
+                               if (!compareecc) {
+                                       /* Some hw ecc generators need to know when the
+                                        * syndrome is read from flash */
+                                       this->enable_hwecc(mtd, NAND_ECC_READSYN);
+                                       this->read_buf(mtd, &oob_data[i], eccbytes);
+                                       /* We calc error correction directly, it checks the hw
+                                        * generator for an error, reads back the syndrome and
+                                        * does the error correction on the fly */
+                                       if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) {
+                                               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " 
+                                                       "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
+                                               ecc_failed++;
+                                       }
+                               } else {
+                                       this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+                               }       
+                       }
+                       break;                                          
+
+               default:
+                       printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+                       BUG();  
+               }
+
+               /* read oobdata */
+               this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
+
+               /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
+               if (!compareecc)
+                       goto readoob;   
+               
+               /* Pick the ECC bytes out of the oob data */
+               for (j = 0; j < oobsel->eccbytes; j++)
+                       ecc_code[j] = oob_data[oob_config[j]];
+
+               /* correct data, if neccecary */
+               for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
+                       ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
+                       
+                       /* Get next chunk of ecc bytes */
+                       j += eccbytes;
+                       
+                       /* Check, if we have a fs supplied oob-buffer, 
+                        * This is the legacy mode. Used by YAFFS1
+                        * Should go away some day
+                        */
+                       if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { 
+                               int *p = (int *)(&oob_data[mtd->oobsize]);
+                               p[i] = ecc_status;
+                       }
+                       
+                       if (ecc_status == -1) { 
+                               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
+                               ecc_failed++;
+                       }
+               }               
+
+       readoob:
+               /* check, if we have a fs supplied oob-buffer */
+               if (oob_buf) {
+                       /* without autoplace. Legacy mode used by YAFFS1 */
+                       switch(oobsel->useecc) {
+                       case MTD_NANDECC_AUTOPLACE:
+                               /* Walk through the autoplace chunks */
+                               for (i = 0, j = 0; j < mtd->oobavail; i++) {
+                                       int from = oobsel->oobfree[i][0];
+                                       int num = oobsel->oobfree[i][1];
+                                       memcpy(&oob_buf[oob], &oob_data[from], num);
+                                       j+= num;
+                               }
+                               oob += mtd->oobavail;
+                               break;
+                       case MTD_NANDECC_PLACE:
+                               /* YAFFS1 legacy mode */
+                               oob_data += this->eccsteps * sizeof (int);
+                       default:
+                               oob_data += mtd->oobsize;
+                       }
+               }
+       readdata:
+               /* Partial page read, transfer data into fs buffer */
+               if (!aligned) { 
+                       for (j = col; j < end && read < len; j++)
+                               buf[read++] = data_poi[j];
+                       this->pagebuf = realpage;       
+               } else          
+                       read += mtd->oobblock;
+
+               /* Apply delay or wait for ready/busy pin 
+                * Do this before the AUTOINCR check, so no problems
+                * arise if a chip which does auto increment
+                * is marked as NOAUTOINCR by the board driver.
+               */
+               if (!this->dev_ready) 
+                       udelay (this->chip_delay);
+               else
+                       while (!this->dev_ready(mtd));  
+                       
+               if (read == len)
+                       break;  
+
+               /* For subsequent reads align to page boundary. */
+               col = 0;
+               /* Increment page address */
+               realpage++;
+
+               page = realpage & this->pagemask;
+               /* Check, if we cross a chip boundary */
+               if (!page) {
+                       chipnr++;
+                       this->select_chip(mtd, -1);
+                       this->select_chip(mtd, chipnr);
+               }
+               /* Check, if the chip supports auto page increment 
+                * or if we have hit a block boundary. 
+               */ 
+               if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+                       sndcmd = 1;                             
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_chip(mtd);
+
+       /*
+        * Return success, if no ECC failures, else -EIO
+        * fs driver will take care of that, because
+        * retlen == desired len and result == -EIO
+        */
+       *retlen = read;
+       return ecc_failed ? -EIO : 0;
+}
+
+/**
+ * nand_read_oob - [MTD Interface] NAND read out-of-band
+ * @mtd:       MTD device structure
+ * @from:      offset to read from
+ * @len:       number of bytes to read
+ * @retlen:    pointer to variable to store the number of read bytes
+ * @buf:       the databuffer to put data
+ *
+ * NAND read out-of-band data from the spare area
+ */
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+{
+       int i, col, page, chipnr;
+       struct nand_chip *this = mtd->priv;
+       int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+       /* Shift to get page */
+       page = (int)(from >> this->page_shift);
+       chipnr = (int)(from >> this->chip_shift);
+       
+       /* Mask to get column */
+       col = from & (mtd->oobsize - 1);
+
+       /* Initialize return length value */
+       *retlen = 0;
+
+       /* Do not allow reads past end of device */
+       if ((from + len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
+               *retlen = 0;
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       nand_get_chip (this, mtd , FL_READING);
+
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+
+       /* Send the read command */
+       this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
+       /* 
+        * Read the data, if we read more than one page
+        * oob data, let the device transfer the data !
+        */
+       i = 0;
+       while (i < len) {
+               int thislen = mtd->oobsize - col;
+               thislen = min_t(int, thislen, len);
+               this->read_buf(mtd, &buf[i], thislen);
+               i += thislen;
+               
+               /* Apply delay or wait for ready/busy pin 
+                * Do this before the AUTOINCR check, so no problems
+                * arise if a chip which does auto increment
+                * is marked as NOAUTOINCR by the board driver.
+               */
+               if (!this->dev_ready) 
+                       udelay (this->chip_delay);
+               else
+                       while (!this->dev_ready(mtd));  
+
+               /* Read more ? */
+               if (i < len) {
+                       page++;
+                       col = 0;
+
+                       /* Check, if we cross a chip boundary */
+                       if (!(page & this->pagemask)) {
+                               chipnr++;
+                               this->select_chip(mtd, -1);
+                               this->select_chip(mtd, chipnr);
+                       }
+                               
+                       /* Check, if the chip supports auto page increment 
+                        * or if we have hit a block boundary. 
+                       */ 
+                       if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
+                               /* For subsequent page reads set offset to 0 */
+                               this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
+                       }
+               }
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_chip(mtd);
+
+       /* Return happy */
+       *retlen = len;
+       return 0;
+}
+
+/**
+ * nand_read_raw - [GENERIC] Read raw data including oob into buffer
+ * @mtd:       MTD device structure
+ * @buf:       temporary buffer
+ * @from:      offset to read from
+ * @len:       number of bytes to read
+ * @ooblen:    number of oob data bytes to read
+ *
+ * Read raw data including oob into buffer
+ */
+int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen)
+{
+       struct nand_chip *this = mtd->priv;
+       int page = (int) (from >> this->page_shift);
+       int chip = (int) (from >> this->chip_shift);
+       int sndcmd = 1;
+       int cnt = 0;
+       int pagesize = mtd->oobblock + mtd->oobsize;
+       int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+       /* Do not allow reads past end of device */
+       if ((from + len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n");
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       nand_get_chip (this, mtd , FL_READING);
+
+       this->select_chip (mtd, chip);
+       
+       /* Add requested oob length */
+       len += ooblen;
+       
+       while (len) {
+               if (sndcmd)
+                       this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
+               sndcmd = 0;     
+
+               this->read_buf (mtd, &buf[cnt], pagesize);
+
+               len -= pagesize;
+               cnt += pagesize;
+               page++;
+               
+               if (!this->dev_ready) 
+                       udelay (this->chip_delay);
+               else
+                       while (!this->dev_ready(mtd));  
+                       
+               /* Check, if the chip supports auto page increment */ 
+               if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+                       sndcmd = 1;
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_chip(mtd);
+       return 0;
+}
+
+
+/** 
+ * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer 
+ * @mtd:       MTD device structure
+ * @fsbuf:     buffer given by fs driver
+ * @oobsel:    out of band selection structre
+ * @autoplace: 1 = place given buffer into the oob bytes
+ * @numpages:  number of pages to prepare
+ *
+ * Return:
+ * 1. Filesystem buffer available and autoplacement is off,
+ *    return filesystem buffer
+ * 2. No filesystem buffer or autoplace is off, return internal
+ *    buffer
+ * 3. Filesystem buffer is given and autoplace selected
+ *    put data from fs buffer into internal buffer and
+ *    retrun internal buffer
+ *
+ * Note: The internal buffer is filled with 0xff. This must
+ * be done only once, when no autoplacement happens
+ * Autoplacement sets the buffer dirty flag, which
+ * forces the 0xff fill before using the buffer again.
+ *
+*/
+static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel,
+               int autoplace, int numpages)
+{
+       struct nand_chip *this = mtd->priv;
+       int i, len, ofs;
+
+       /* Zero copy fs supplied buffer */
+       if (fsbuf && !autoplace) 
+               return fsbuf;
+
+       /* Check, if the buffer must be filled with ff again */
+       if (this->oobdirty) {   
+               memset (this->oob_buf, 0xff, 
+                       mtd->oobsize << (this->phys_erase_shift - this->page_shift));
+               this->oobdirty = 0;
+       }       
+       
+       /* If we have no autoplacement or no fs buffer use the internal one */
+       if (!autoplace || !fsbuf)
+               return this->oob_buf;
+       
+       /* Walk through the pages and place the data */
+       this->oobdirty = 1;
+       ofs = 0;
+       while (numpages--) {
+               for (i = 0, len = 0; len < mtd->oobavail; i++) {
+                       int to = ofs + oobsel->oobfree[i][0];
+                       int num = oobsel->oobfree[i][1];
+                       memcpy (&this->oob_buf[to], fsbuf, num);
+                       len += num;
+                       fsbuf += num;
+               }
+               ofs += mtd->oobavail;
+       }
+       return this->oob_buf;
+}
+
+#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
+
+/**
+ * nand_write - [MTD Interface] compability function for nand_write_ecc
+ * @mtd:       MTD device structure
+ * @to:                offset to write to
+ * @len:       number of bytes to write
+ * @retlen:    pointer to variable to store the number of written bytes
+ * @buf:       the data to write
+ *
+ * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL
+ *
+*/
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+{
+       return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
+}
+                          
+/**
+ * nand_write_ecc - [MTD Interface] NAND write with ECC
+ * @mtd:       MTD device structure
+ * @to:                offset to write to
+ * @len:       number of bytes to write
+ * @retlen:    pointer to variable to store the number of written bytes
+ * @buf:       the data to write
+ * @eccbuf:    filesystem supplied oob data buffer
+ * @oobsel:    oob selection structure
+ *
+ * NAND write with ECC
+ */
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+                          size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
+{
+       int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr;
+       int autoplace = 0, numpages, totalpages;
+       struct nand_chip *this = mtd->priv;
+       u_char *oobbuf, *bufstart;
+       int     ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+       /* Initialize retlen, in case of early exit */
+       *retlen = 0;
+
+       /* Do not allow write past end of device */
+       if ((to + len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
+               return -EINVAL;
+       }
+
+       /* reject writes, which are not page aligned */ 
+       if (NOTALIGNED (to) || NOTALIGNED(len)) {
+               printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       nand_get_chip (this, mtd, FL_WRITING);
+
+       /* Calculate chipnr */
+       chipnr = (int)(to >> this->chip_shift);
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd))
+               goto out;
+
+       /* if oobsel is NULL, use chip defaults */
+       if (oobsel == NULL) 
+               oobsel = &mtd->oobinfo;         
+               
+       /* Autoplace of oob data ? Use the default placement scheme */
+       if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
+               oobsel = this->autooob;
+               autoplace = 1;
+       }       
+
+       /* Setup variables and oob buffer */
+       totalpages = len >> this->page_shift;
+       page = (int) (to >> this->page_shift);
+       /* Invalidate the page cache, if we write to the cached page */
+       if (page <= this->pagebuf && this->pagebuf < (page + totalpages))  
+               this->pagebuf = -1;
+       
+       /* Set it relative to chip */
+       page &= this->pagemask;
+       startpage = page;
+       /* Calc number of pages we can write in one go */
+       numpages = min (ppblock - (startpage  & (ppblock - 1)), totalpages);
+       oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
+       bufstart = (u_char *)buf;
+
+       /* Loop until all data is written */
+       while (written < len) {
+
+               this->data_poi = (u_char*) &buf[written];
+               /* Write one page. If this is the last page to write
+                * or the last page in this block, then use the
+                * real pageprogram command, else select cached programming
+                * if supported by the chip.
+                */
+               ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));
+               if (ret) {
+                       DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret);
+                       goto out;
+               }       
+               /* Next oob page */
+               oob += mtd->oobsize;
+               /* Update written bytes count */
+               written += mtd->oobblock;
+               if (written == len) 
+                       goto cmp;
+               
+               /* Increment page address */
+               page++;
+
+               /* Have we hit a block boundary ? Then we have to verify and
+                * if verify is ok, we have to setup the oob buffer for
+                * the next pages.
+               */
+               if (!(page & (ppblock - 1))){
+                       int ofs;
+                       this->data_poi = bufstart;
+                       ret = nand_verify_pages (mtd, this, startpage, 
+                               page - startpage,
+                               oobbuf, oobsel, chipnr, (eccbuf != NULL));
+                       if (ret) {
+                               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
+                               goto out;
+                       }       
+                       *retlen = written;
+
+                       ofs = autoplace ? mtd->oobavail : mtd->oobsize;
+                       if (eccbuf)
+                               eccbuf += (page - startpage) * ofs;
+                       totalpages -= page - startpage;
+                       numpages = min (totalpages, ppblock);
+                       page &= this->pagemask;
+                       startpage = page;
+                       oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, 
+                                       autoplace, numpages);
+                       /* Check, if we cross a chip boundary */
+                       if (!page) {
+                               chipnr++;
+                               this->select_chip(mtd, -1);
+                               this->select_chip(mtd, chipnr);
+                       }
+               }
+       }
+       /* Verify the remaining pages */
+cmp:
+       this->data_poi = bufstart;
+       ret = nand_verify_pages (mtd, this, startpage, totalpages,
+               oobbuf, oobsel, chipnr, (eccbuf != NULL));
+       if (!ret)
+               *retlen = written;
+       else    
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
+
+out:
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_chip(mtd);
+
+       return ret;
+}
+
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write out-of-band
+ * @mtd:       MTD device structure
+ * @to:                offset to write to
+ * @len:       number of bytes to write
+ * @retlen:    pointer to variable to store the number of written bytes
+ * @buf:       the data to write
+ *
+ * NAND write out-of-band
+ */
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+{
+       int column, page, status, ret = -EIO, chipnr;
+       struct nand_chip *this = mtd->priv;
+
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+       /* Shift to get page */
+       page = (int) (to >> this->page_shift);
+       chipnr = (int) (to >> this->chip_shift);
+
+       /* Mask to get column */
+       column = to & (mtd->oobsize - 1);
+
+       /* Initialize return length value */
+       *retlen = 0;
+
+       /* Do not allow write past end of page */
+       if ((column + len) > mtd->oobsize) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       nand_get_chip (this, mtd, FL_WRITING);
+
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+
+       /* Reset the chip. Some chips (like the Toshiba TC5832DC found
+          in one of my DiskOnChip 2000 test units) will clear the whole
+          data page too if we don't do this. I have no clue why, but
+          I seem to have 'fixed' it in the doc2000 driver in
+          August 1999.  dwmw2. */
+       this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd))
+               goto out;
+       
+       /* Invalidate the page cache, if we write to the cached page */
+       if (page == this->pagebuf)
+               this->pagebuf = -1;
+
+       if (NAND_MUST_PAD(this)) {
+               /* Write out desired data */
+               this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
+               /* prepad 0xff for partial programming */
+               this->write_buf(mtd, ffchars, column);
+               /* write data */
+               this->write_buf(mtd, buf, len);
+               /* postpad 0xff for partial programming */
+               this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
+       } else {
+               /* Write out desired data */
+               this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
+               /* write data */
+               this->write_buf(mtd, buf, len);
+       }
+       /* Send command to program the OOB data */
+       this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+       status = this->waitfunc (mtd, this, FL_WRITING);
+
+       /* See if device thinks it succeeded */
+       if (status & 0x01) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
+               ret = -EIO;
+               goto out;
+       }
+       /* Return happy */
+       *retlen = len;
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+       /* Send command to read back the data */
+       this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
+
+       if (this->verify_buf(mtd, buf, len)) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
+               ret = -EIO;
+               goto out;
+       }
+#endif
+       ret = 0;
+out:
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_chip(mtd);
+
+       return ret;
+}
+
+
+/**
+ * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc
+ * @mtd:       MTD device structure
+ * @vecs:      the iovectors to write
+ * @count:     number of vectors
+ * @to:                offset to write to
+ * @retlen:    pointer to variable to store the number of written bytes
+ *
+ * NAND write with kvec. This just calls the ecc function
+ */
+static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, 
+               loff_t to, size_t * retlen)
+{
+       return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL));    
+}
+
+/**
+ * nand_writev_ecc - [MTD Interface] write with iovec with ecc
+ * @mtd:       MTD device structure
+ * @vecs:      the iovectors to write
+ * @count:     number of vectors
+ * @to:                offset to write to
+ * @retlen:    pointer to variable to store the number of written bytes
+ * @eccbuf:    filesystem supplied oob data buffer
+ * @oobsel:    oob selection structure
+ *
+ * NAND write with iovec with ecc
+ */
+static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, 
+               loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
+{
+       int i, page, len, total_len, ret = -EIO, written = 0, chipnr;
+       int oob, numpages, autoplace = 0, startpage;
+       struct nand_chip *this = mtd->priv;
+       int     ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+       u_char *oobbuf, *bufstart;
+
+       /* Preset written len for early exit */
+       *retlen = 0;
+
+       /* Calculate total length of data */
+       total_len = 0;
+       for (i = 0; i < count; i++)
+               total_len += (int) vecs[i].iov_len;
+
+       DEBUG (MTD_DEBUG_LEVEL3,
+              "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
+
+       /* Do not allow write past end of page */
+       if ((to + total_len) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
+               return -EINVAL;
+       }
+
+       /* reject writes, which are not page aligned */ 
+       if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
+               printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       nand_get_chip (this, mtd, FL_WRITING);
+
+       /* Get the current chip-nr */
+       chipnr = (int) (to >> this->chip_shift);
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd))
+               goto out;
+
+       /* if oobsel is NULL, use chip defaults */
+       if (oobsel == NULL) 
+               oobsel = &mtd->oobinfo;         
+
+       /* Autoplace of oob data ? Use the default placement scheme */
+       if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
+               oobsel = this->autooob;
+               autoplace = 1;
+       }       
+
+       /* Setup start page */
+       page = (int) (to >> this->page_shift);
+       /* Invalidate the page cache, if we write to the cached page */
+       if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift))  
+               this->pagebuf = -1;
+
+       startpage = page & this->pagemask;
+
+       /* Loop until all kvec' data has been written */
+       len = 0;
+       while (count) {
+               /* If the given tuple is >= pagesize then
+                * write it out from the iov
+                */
+               if ((vecs->iov_len - len) >= mtd->oobblock) {
+                       /* Calc number of pages we can write
+                        * out of this iov in one go */
+                       numpages = (vecs->iov_len - len) >> this->page_shift;
+                       /* Do not cross block boundaries */
+                       numpages = min (ppblock - (startpage & (ppblock - 1)), numpages);
+                       oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+                       bufstart = (u_char *)vecs->iov_base;
+                       bufstart += len;
+                       this->data_poi = bufstart;
+                       oob = 0;
+                       for (i = 1; i <= numpages; i++) {
+                               /* Write one page. If this is the last page to write
+                                * then use the real pageprogram command, else select 
+                                * cached programming if supported by the chip.
+                                */
+                               ret = nand_write_page (mtd, this, page & this->pagemask, 
+                                       &oobbuf[oob], oobsel, i != numpages);
+                               if (ret)
+                                       goto out;
+                               this->data_poi += mtd->oobblock;
+                               len += mtd->oobblock;
+                               oob += mtd->oobsize;
+                               page++;
+                       }
+                       /* Check, if we have to switch to the next tuple */
+                       if (len >= (int) vecs->iov_len) {
+                               vecs++;
+                               len = 0;
+                               count--;
+                       }
+               } else {
+                       /* We must use the internal buffer, read data out of each 
+                        * tuple until we have a full page to write
+                        */
+                       int cnt = 0;
+                       while (cnt < mtd->oobblock) {
+                               if (vecs->iov_base != NULL && vecs->iov_len) 
+                                       this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
+                               /* Check, if we have to switch to the next tuple */
+                               if (len >= (int) vecs->iov_len) {
+                                       vecs++;
+                                       len = 0;
+                                       count--;
+                               }
+                       }
+                       this->pagebuf = page;   
+                       this->data_poi = this->data_buf;        
+                       bufstart = this->data_poi;
+                       numpages = 1;           
+                       oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+                       ret = nand_write_page (mtd, this, page & this->pagemask,
+                               oobbuf, oobsel, 0);
+                       if (ret)
+                               goto out;
+                       page++;
+               }
+
+               this->data_poi = bufstart;
+               ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);
+               if (ret)
+                       goto out;
+                       
+               written += mtd->oobblock * numpages;
+               /* All done ? */
+               if (!count)
+                       break;
+
+               startpage = page & this->pagemask;
+               /* Check, if we cross a chip boundary */
+               if (!startpage) {
+                       chipnr++;
+                       this->select_chip(mtd, -1);
+                       this->select_chip(mtd, chipnr);
+               }
+       }
+       ret = 0;
+out:
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_chip(mtd);
+
+       *retlen = written;
+       return ret;
+}
+
+/**
+ * single_erease_cmd - [GENERIC] NAND standard block erase command function
+ * @mtd:       MTD device structure
+ * @page:      the page address of the block which will be erased
+ *
+ * Standard erase command for NAND chips
+ */
+static void single_erase_cmd (struct mtd_info *mtd, int page)
+{
+       struct nand_chip *this = mtd->priv;
+       /* Send commands to erase a block */
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+       this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * multi_erease_cmd - [GENERIC] AND specific block erase command function
+ * @mtd:       MTD device structure
+ * @page:      the page address of the block which will be erased
+ *
+ * AND multi block erase command function
+ * Erase 4 consecutive blocks
+ */
+static void multi_erase_cmd (struct mtd_info *mtd, int page)
+{
+       struct nand_chip *this = mtd->priv;
+       /* Send commands to erase a block */
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+       this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+       this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd:       MTD device structure
+ * @instr:     erase instruction
+ *
+ * Erase one ore more blocks
+ */
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+       return nand_erase_nand (mtd, instr, 0);
+}
+/**
+ * nand_erase_intern - [NAND Interface] erase block(s)
+ * @mtd:       MTD device structure
+ * @instr:     erase instruction
+ * @allowbbt:  allow erasing the bbt area
+ *
+ * Erase one ore more blocks
+ */
+int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
+{
+       int page, len, status, pages_per_block, ret, chipnr;
+       struct nand_chip *this = mtd->priv;
+
+       DEBUG (MTD_DEBUG_LEVEL3,
+              "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
+
+       /* Start address must align on block boundary */
+       if (instr->addr & ((1 << this->phys_erase_shift) - 1)) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+               return -EINVAL;
+       }
+
+       /* Length must align on block boundary */
+       if (instr->len & ((1 << this->phys_erase_shift) - 1)) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
+               return -EINVAL;
+       }
+
+       /* Do not allow erase past end of device */
+       if ((instr->len + instr->addr) > mtd->size) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
+               return -EINVAL;
+       }
+
+       instr->fail_addr = 0xffffffff;
+
+       /* Grab the lock and see if the device is available */
+       nand_get_chip (this, mtd, FL_ERASING);
+
+       /* Shift to get first page */
+       page = (int) (instr->addr >> this->page_shift);
+       chipnr = (int) (instr->addr >> this->chip_shift);
+
+       /* Calculate pages in each block */
+       pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
+
+       /* Select the NAND device */
+       this->select_chip(mtd, chipnr);
+
+       /* Check the WP bit */
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd)) {
+               DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
+               instr->state = MTD_ERASE_FAILED;
+               goto erase_exit;
+       }
+
+       /* Loop through the pages */
+       len = instr->len;
+
+       instr->state = MTD_ERASING;
+
+       while (len) {
+               /* Check if we have a bad block, we do not erase bad blocks ! */
+               if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
+                       printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
+                       instr->state = MTD_ERASE_FAILED;
+                       goto erase_exit;
+               }
+               
+               /* Invalidate the page cache, if we erase the block which contains 
+                  the current cached page */
+               if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
+                       this->pagebuf = -1;
+
+               this->erase_cmd (mtd, page & this->pagemask);
+               
+               status = this->waitfunc (mtd, this, FL_ERASING);
+
+               /* See if block erase succeeded */
+               if (status & 0x01) {
+                       DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
+                       instr->state = MTD_ERASE_FAILED;
+                       instr->fail_addr = (page << this->page_shift);
+                       goto erase_exit;
+               }
+               
+               /* Increment page address and decrement length */
+               len -= (1 << this->phys_erase_shift);
+               page += pages_per_block;
+
+               /* Check, if we cross a chip boundary */
+               if (len && !(page & this->pagemask)) {
+                       chipnr++;
+                       this->select_chip(mtd, -1);
+                       this->select_chip(mtd, chipnr);
+               }
+       }
+       instr->state = MTD_ERASE_DONE;
+
+erase_exit:
+
+       ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+       /* Do call back function */
+       if (!ret && instr->callback)
+               instr->callback (instr);
+
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_chip(mtd);
+
+       /* Return more or less happy */
+       return ret;
+}
+
+/**
+ * nand_sync - [MTD Interface] sync
+ * @mtd:       MTD device structure
+ *
+ * Sync is actually a wait for chip ready function
+ */
+static void nand_sync (struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       DECLARE_WAITQUEUE (wait, current);
+
+       DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+
+retry:
+       /* Grab the spinlock */
+       spin_lock_bh (&this->chip_lock);
+
+       /* See what's going on */
+       switch (this->state) {
+       case FL_READY:
+       case FL_SYNCING:
+               this->state = FL_SYNCING;
+               spin_unlock_bh (&this->chip_lock);
+               break;
+
+       default:
+               /* Not an idle state */
+               add_wait_queue (&this->wq, &wait);
+               spin_unlock_bh (&this->chip_lock);
+               schedule ();
+
+               remove_wait_queue (&this->wq, &wait);
+               goto retry;
+       }
+
+       /* Lock the device */
+       spin_lock_bh (&this->chip_lock);
+
+       /* Set the device to be ready again */
+       if (this->state == FL_SYNCING) {
+               this->state = FL_READY;
+               wake_up (&this->wq);
+       }
+
+       /* Unlock the device */
+       spin_unlock_bh (&this->chip_lock);
+}
+
+
+/**
+ * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
+ * @mtd:       MTD device structure
+ * @ofs:       offset relative to mtd start
+ */
+static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
+{
+       /* Check for invalid offset */
+       if (ofs > mtd->size) 
+               return -EINVAL;
+       
+       return nand_block_checkbad (mtd, ofs, 1, 0);
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
+ * @mtd:       MTD device structure
+ * @ofs:       offset relative to mtd start
+ */
+static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
+{
+       struct nand_chip *this = mtd->priv;
+       int ret;
+
+        if ((ret = nand_block_isbad(mtd, ofs))) {
+               /* If it was bad already, return success and do nothing. */
+               if (ret > 0)
+                       return 0;
+               return ret;
+        }
+
+       return this->block_markbad(mtd, ofs);
+}
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd:       MTD device structure
+ * @maxchips:  Number of chips to scan for
+ *
+ * This fills out all the not initialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values. Buffers are allocated if
+ * they are not provided by the board driver
+ *
+ */
+int nand_scan (struct mtd_info *mtd, int maxchips)
+{
+       int i, j, nand_maf_id, nand_dev_id, busw;
+       struct nand_chip *this = mtd->priv;
+
+       /* Get buswidth to select the correct functions*/
+       busw = this->options & NAND_BUSWIDTH_16;
+
+       /* check for proper chip_delay setup, set 20us if not */
+       if (!this->chip_delay)
+               this->chip_delay = 20;
+
+       /* check, if a user supplied command function given */
+       if (this->cmdfunc == NULL)
+               this->cmdfunc = nand_command;
+
+       /* check, if a user supplied wait function given */
+       if (this->waitfunc == NULL)
+               this->waitfunc = nand_wait;
+
+       if (!this->select_chip)
+               this->select_chip = nand_select_chip;
+       if (!this->write_byte)
+               this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
+       if (!this->read_byte)
+               this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
+       if (!this->write_word)
+               this->write_word = nand_write_word;
+       if (!this->read_word)
+               this->read_word = nand_read_word;
+       if (!this->block_bad)
+               this->block_bad = nand_block_bad;
+       if (!this->block_markbad)
+               this->block_markbad = nand_default_block_markbad;
+       if (!this->write_buf)
+               this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+       if (!this->read_buf)
+               this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+       if (!this->verify_buf)
+               this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+       if (!this->scan_bbt)
+               this->scan_bbt = nand_default_bbt;
+
+       /* Select the device */
+       this->select_chip(mtd, 0);
+
+       /* Send the command for reading device ID */
+       this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+
+       /* Read manufacturer and device IDs */
+       nand_maf_id = this->read_byte(mtd);
+       nand_dev_id = this->read_byte(mtd);
+
+       /* Print and store flash device information */
+       for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+                               
+               if (nand_dev_id != nand_flash_ids[i].id) 
+                       continue;
+
+               if (!mtd->name) mtd->name = nand_flash_ids[i].name;
+               this->chipsize = nand_flash_ids[i].chipsize << 20;
+               
+               /* New devices have all the information in additional id bytes */
+               if (!nand_flash_ids[i].pagesize) {
+                       int extid;
+                       /* The 3rd id byte contains non relevant data ATM */
+                       extid = this->read_byte(mtd);
+                       /* The 4th id byte is the important one */
+                       extid = this->read_byte(mtd);
+                       /* Calc pagesize */
+                       mtd->oobblock = 1024 << (extid & 0x3);
+                       extid >>= 2;
+                       /* Calc oobsize */
+                       mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512);
+                       extid >>= 2;
+                       /* Calc blocksize. Blocksize is multiples of 64KiB */
+                       mtd->erasesize = (64 * 1024)  << (extid & 0x03);
+                       extid >>= 2;
+                       /* Get buswidth information */
+                       busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+               
+               } else {
+                       /* Old devices have this data hardcoded in the
+                        * device id table */
+                       mtd->erasesize = nand_flash_ids[i].erasesize;
+                       mtd->oobblock = nand_flash_ids[i].pagesize;
+                       mtd->oobsize = mtd->oobblock / 32;
+                       busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
+               }
+
+               /* Check, if buswidth is correct. Hardware drivers should set
+                * this correct ! */
+               if (busw != (this->options & NAND_BUSWIDTH_16)) {
+                       printk (KERN_INFO "NAND device: Manufacturer ID:"
+                               " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, 
+                               nand_manuf_ids[i].name , mtd->name);
+                       printk (KERN_WARNING 
+                               "NAND bus width %d instead %d bit\n", 
+                                       (this->options & NAND_BUSWIDTH_16) ? 16 : 8,
+                                       busw ? 16 : 8);
+                       this->select_chip(mtd, -1);
+                       return 1;       
+               }
+               
+               /* Calculate the address shift from the page size */    
+               this->page_shift = ffs(mtd->oobblock) - 1;
+               this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
+               this->chip_shift = ffs(this->chipsize) - 1;
+
+               /* Set the bad block position */
+               this->badblockpos = mtd->oobblock > 512 ? 
+                       NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+
+               /* Get chip options, preserve non chip based options */
+               this->options &= ~NAND_CHIPOPTIONS_MSK;
+               this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
+               /* Set this as a default. Board drivers can override it, if neccecary */
+               this->options |= NAND_NO_AUTOINCR;
+               /* Check if this is a not a samsung device. Do not clear the options
+                * for chips which are not having an extended id.
+                */     
+               if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
+                       this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+               
+               /* Check for AND chips with 4 page planes */
+               if (this->options & NAND_4PAGE_ARRAY)
+                       this->erase_cmd = multi_erase_cmd;
+               else
+                       this->erase_cmd = single_erase_cmd;
+
+               /* Do not replace user supplied command function ! */
+               if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
+                       this->cmdfunc = nand_command_lp;
+                               
+               /* Try to identify manufacturer */
+               for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
+                       if (nand_manuf_ids[j].id == nand_maf_id)
+                               break;
+               }
+               printk (KERN_INFO "NAND device: Manufacturer ID:"
+                       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, 
+                       nand_manuf_ids[j].name , nand_flash_ids[i].name);
+               break;
+       }
+
+       if (!nand_flash_ids[i].name) {
+               printk (KERN_WARNING "No NAND device found!!!\n");
+               this->select_chip(mtd, -1);
+               return 1;
+       }
+
+       for (i=1; i < maxchips; i++) {
+               this->select_chip(mtd, i);
+
+               /* Send the command for reading device ID */
+               this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+
+               /* Read manufacturer and device IDs */
+               if (nand_maf_id != this->read_byte(mtd) ||
+                   nand_dev_id != this->read_byte(mtd))
+                       break;
+       }
+       if (i > 1)
+               printk(KERN_INFO "%d NAND chips detected\n", i);
+       
+       /* Allocate buffers, if neccecary */
+       if (!this->oob_buf) {
+               size_t len;
+               len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
+               this->oob_buf = kmalloc (len, GFP_KERNEL);
+               if (!this->oob_buf) {
+                       printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
+                       return -ENOMEM;
+               }
+               this->options |= NAND_OOBBUF_ALLOC;
+       }
+       
+       if (!this->data_buf) {
+               size_t len;
+               len = mtd->oobblock + mtd->oobsize;
+               this->data_buf = kmalloc (len, GFP_KERNEL);
+               if (!this->data_buf) {
+                       if (this->options & NAND_OOBBUF_ALLOC)
+                               kfree (this->oob_buf);
+                       printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
+                       return -ENOMEM;
+               }
+               this->options |= NAND_DATABUF_ALLOC;
+       }
+
+       /* Store the number of chips and calc total size for mtd */
+       this->numchips = i;
+       mtd->size = i * this->chipsize;
+       /* Convert chipsize to number of pages per chip -1. */
+       this->pagemask = (this->chipsize >> this->page_shift) - 1;
+       /* Preset the internal oob buffer */
+       memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
+
+       /* If no default placement scheme is given, select an
+        * appropriate one */
+       if (!this->autooob) {
+               /* Select the appropriate default oob placement scheme for
+                * placement agnostic filesystems */
+               switch (mtd->oobsize) { 
+               case 8:
+                       this->autooob = &nand_oob_8;
+                       break;
+               case 16:
+                       this->autooob = &nand_oob_16;
+                       break;
+               case 64:
+                       this->autooob = &nand_oob_64;
+                       break;
+               default:
+                       printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
+                               mtd->oobsize);
+                       BUG();
+               }
+       }
+       
+       /* The number of bytes available for the filesystem to place fs dependend
+        * oob data */
+       if (this->options & NAND_BUSWIDTH_16) {
+               mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
+               if (this->autooob->eccbytes & 0x01)
+                       mtd->oobavail--;
+       } else
+               mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);
+
+       /* 
+        * check ECC mode, default to software
+        * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
+        * fallback to software ECC 
+       */
+       this->eccsize = 256;    /* set default eccsize */       
+
+       switch (this->eccmode) {
+
+       case NAND_ECC_HW3_512: 
+       case NAND_ECC_HW6_512: 
+       case NAND_ECC_HW8_512: 
+               if (mtd->oobblock == 256) {
+                       printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
+                       this->eccmode = NAND_ECC_SOFT;
+                       this->calculate_ecc = nand_calculate_ecc;
+                       this->correct_data = nand_correct_data;
+                       break;          
+               } else 
+                       this->eccsize = 512; /* set eccsize to 512 and fall through for function check */
+
+       case NAND_ECC_HW3_256:
+               if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
+                       break;
+               printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
+               BUG();  
+
+       case NAND_ECC_NONE: 
+               printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
+               this->eccmode = NAND_ECC_NONE;
+               break;
+
+       case NAND_ECC_SOFT:     
+               this->calculate_ecc = nand_calculate_ecc;
+               this->correct_data = nand_correct_data;
+               break;
+
+       default:
+               printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+               BUG();  
+       }       
+       
+       mtd->eccsize = this->eccsize;
+       
+       /* Set the number of read / write steps for one page to ensure ECC generation */
+       switch (this->eccmode) {
+       case NAND_ECC_HW3_512:
+       case NAND_ECC_HW6_512:
+       case NAND_ECC_HW8_512:
+               this->eccsteps = mtd->oobblock / 512;
+               break;
+       case NAND_ECC_HW3_256:
+       case NAND_ECC_SOFT:     
+               this->eccsteps = mtd->oobblock / 256;
+               break;
+               
+       case NAND_ECC_NONE: 
+               this->eccsteps = 1;
+               break;
+       }
+       
+       /* Initialize state, waitqueue and spinlock */
+       this->state = FL_READY;
+       init_waitqueue_head (&this->wq);
+       spin_lock_init (&this->chip_lock);
+
+       /* De-select the device */
+       this->select_chip(mtd, -1);
+
+       /* Invalidate the pagebuffer reference */
+       this->pagebuf = -1;
+
+       /* Fill in remaining MTD driver data */
+       mtd->type = MTD_NANDFLASH;
+       mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+       mtd->ecctype = MTD_ECC_SW;
+       mtd->erase = nand_erase;
+       mtd->point = NULL;
+       mtd->unpoint = NULL;
+       mtd->read = nand_read;
+       mtd->write = nand_write;
+       mtd->read_ecc = nand_read_ecc;
+       mtd->write_ecc = nand_write_ecc;
+       mtd->read_oob = nand_read_oob;
+       mtd->write_oob = nand_write_oob;
+       mtd->readv = NULL;
+       mtd->writev = nand_writev;
+       mtd->writev_ecc = nand_writev_ecc;
+       mtd->sync = nand_sync;
+       mtd->lock = NULL;
+       mtd->unlock = NULL;
+       mtd->suspend = NULL;
+       mtd->resume = NULL;
+       mtd->block_isbad = nand_block_isbad;
+       mtd->block_markbad = nand_block_markbad;
+
+       /* and make the autooob the default one */
+       memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
+
+       mtd->owner = THIS_MODULE;
+
+       /* Build bad block table */
+       return this->scan_bbt (mtd);
+}
+
+/**
+ * nand_release - [NAND Interface] Free resources held by the NAND device 
+ * @mtd:       MTD device structure
+*/
+void nand_release (struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+
+#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+       /* Unregister partitions */
+       del_mtd_partitions (mtd);
+#endif
+       /* Unregister the device */
+       del_mtd_device (mtd);
+
+       /* Free bad block table memory, if allocated */
+       if (this->bbt)
+               kfree (this->bbt);
+       /* Buffer allocated by nand_scan ? */
+       if (this->options & NAND_OOBBUF_ALLOC)
+               kfree (this->oob_buf);
+       /* Buffer allocated by nand_scan ? */
+       if (this->options & NAND_DATABUF_ALLOC)
+               kfree (this->data_buf);
+}
+
+EXPORT_SYMBOL (nand_scan);
+EXPORT_SYMBOL (nand_release);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION ("Generic NAND flash driver code");
diff --git a/drivers/mtd/nand/tx4925ndfmc.c b/drivers/mtd/nand/tx4925ndfmc.c
new file mode 100644 (file)
index 0000000..94a6165
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ *  drivers/mtd/tx4925ndfmc.c
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device found on the
+ *   Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports 
+ *   16MiB, 32MiB and 64MiB cards.
+ *
+ * Author: MontaVista Software, Inc.  source@mvista.com
+ *
+ * Derived from drivers/mtd/autcpu12.c
+ *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * $Id: tx4925ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
+ *
+ * Copyright (C) 2001 Toshiba Corporation 
+ * 
+ * 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/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/tx4925/tx4925_nand.h>
+
+extern struct nand_oobinfo jffs2_oobinfo;
+
+/*
+ * MTD structure for RBTX4925 board
+ */
+static struct mtd_info *tx4925ndfmc_mtd = NULL;
+
+/*
+ * Module stuff
+ */
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define tx4925ndfmc_init init_module
+#define tx4925ndfmc_cleanup cleanup_module
+#endif
+
+/*
+ * Define partitions for flash devices
+ */
+
+static struct mtd_partition partition_info16k[] = {
+       { .name = "RBTX4925 flash partition 1",
+         .offset =  0,
+         .size =    8 * 0x00100000 },
+       { .name = "RBTX4925 flash partition 2",
+         .offset =  8 * 0x00100000,
+         .size =    8 * 0x00100000 },
+};
+
+static struct mtd_partition partition_info32k[] = {
+       { .name = "RBTX4925 flash partition 1",
+         .offset =  0,
+         .size =    8 * 0x00100000 },
+       { .name = "RBTX4925 flash partition 2",
+         .offset = 8 * 0x00100000,
+         .size =  24 * 0x00100000 },
+};
+
+static struct mtd_partition partition_info64k[] = {
+       { .name = "User FS",
+         .offset =  0,
+         .size =   16 * 0x00100000 },
+       { .name = "RBTX4925 flash partition 2",
+         .offset = 16 * 0x00100000,
+         .size =   48 * 0x00100000},
+};
+
+static struct mtd_partition partition_info128k[] = {
+       { .name = "Skip bad section",
+         .offset =  0,
+         .size =   16 * 0x00100000 },
+       { .name = "User FS",
+         .offset = 16 * 0x00100000,
+         .size =   112 * 0x00100000 },
+};
+#define NUM_PARTITIONS16K  2
+#define NUM_PARTITIONS32K  2
+#define NUM_PARTITIONS64K  2
+#define NUM_PARTITIONS128K 2
+
+/* 
+ *     hardware specific access to control-lines
+*/
+static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+
+       switch(cmd){
+
+               case NAND_CTL_SETCLE: 
+                       tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE;
+                       break;
+               case NAND_CTL_CLRCLE:
+                       tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE;
+                       break;
+               case NAND_CTL_SETALE:
+                       tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE;
+                       break;
+               case NAND_CTL_CLRALE: 
+                       tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE;
+                       break;
+               case NAND_CTL_SETNCE:
+                       tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE;
+                       break;
+               case NAND_CTL_CLRNCE:
+                       tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE;
+                       break;
+               case NAND_CTL_SETWP:
+                       tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE;
+                       break;
+               case NAND_CTL_CLRWP:
+                       tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE;
+                       break;
+       }
+}
+
+/*
+*      read device ready pin
+*/
+static int tx4925ndfmc_device_ready(struct mtd_info *mtd)
+{
+       int ready;
+       ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1;
+       return ready;
+}
+void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       /* reset first */
+       tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK;
+       tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
+       tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB;
+}
+static void tx4925ndfmc_disable_ecc(void)
+{
+       tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
+}
+static void tx4925ndfmc_enable_read_ecc(void)
+{
+       tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
+       tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ;
+}
+void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){
+       int i;
+       u_char *ecc = ecc_code;
+        tx4925ndfmc_enable_read_ecc();
+       for (i = 0;i < 6;i++,ecc++)
+               *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr));
+        tx4925ndfmc_disable_ecc();
+}
+void tx4925ndfmc_device_setup(void)
+{
+
+       *(unsigned char *)0xbb005000 &= ~0x08;
+
+        /* reset NDFMC */
+        tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST;
+       while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST);       
+
+       /* setup BusSeparete, Hold Time, Strobe Pulse Width */
+       tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0;
+       tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW;             
+}
+static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd)
+{
+        struct nand_chip *this = mtd->priv;
+        return tx4925_read_nfmc(this->IO_ADDR_R);
+}
+
+static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+        struct nand_chip *this = mtd->priv;
+        tx4925_write_nfmc(byte, this->IO_ADDR_W);
+}
+
+static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+
+       for (i=0; i<len; i++)
+               tx4925_write_nfmc(buf[i], this->IO_ADDR_W);
+}
+
+static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+
+       for (i=0; i<len; i++)
+               buf[i] = tx4925_read_nfmc(this->IO_ADDR_R);
+}
+
+static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+
+       for (i=0; i<len; i++)
+               if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R))
+                       return -EFAULT;
+
+       return 0;
+}
+
+/*
+ * Send command to NAND device
+ */
+static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+       register struct nand_chip *this = mtd->priv;
+
+       /* Begin command latch cycle */
+       this->hwcontrol(mtd, NAND_CTL_SETCLE);
+       /*
+        * Write out the command to the device.
+        */
+       if (command == NAND_CMD_SEQIN) {
+               int readcmd;
+
+               if (column >= mtd->oobblock) {
+                       /* OOB area */
+                       column -= mtd->oobblock;
+                       readcmd = NAND_CMD_READOOB;
+               } else if (column < 256) {
+                       /* First 256 bytes --> READ0 */
+                       readcmd = NAND_CMD_READ0;
+               } else {
+                       column -= 256;
+                       readcmd = NAND_CMD_READ1;
+               }
+               this->write_byte(mtd, readcmd);
+       }
+       this->write_byte(mtd, command);
+
+       /* Set ALE and clear CLE to start address cycle */
+       this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+       if (column != -1 || page_addr != -1) {
+               this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+               /* Serially input address */
+               if (column != -1)
+                       this->write_byte(mtd, column);
+               if (page_addr != -1) {
+                       this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+                       this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+                       /* One more address cycle for higher density devices */
+                       if (mtd->size & 0x0c000000) 
+                               this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
+               }
+               /* Latch in address */
+               this->hwcontrol(mtd, NAND_CTL_CLRALE);
+       }
+       
+       /* 
+        * program and erase have their own busy handlers 
+        * status and sequential in needs no delay
+       */
+       switch (command) {
+                       
+       case NAND_CMD_PAGEPROG:
+               /* Turn off WE */
+               this->hwcontrol (mtd, NAND_CTL_CLRWP);
+                return;
+
+       case NAND_CMD_SEQIN:
+               /* Turn on WE */
+               this->hwcontrol (mtd, NAND_CTL_SETWP);
+                return;
+
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_STATUS:
+               return;
+
+       case NAND_CMD_RESET:
+               if (this->dev_ready)    
+                       break;
+               this->hwcontrol(mtd, NAND_CTL_SETCLE);
+               this->write_byte(mtd, NAND_CMD_STATUS);
+               this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+               while ( !(this->read_byte(mtd) & 0x40));
+               return;
+
+       /* This applies to read commands */     
+       default:
+               /* 
+                * If we don't have access to the busy pin, we apply the given
+                * command delay
+               */
+               if (!this->dev_ready) {
+                       udelay (this->chip_delay);
+                       return;
+               }       
+       }
+       
+       /* wait until command is processed */
+       while (!this->dev_ready(mtd));
+}
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio
+n **pparts, char *);
+#endif
+
+/*
+ * Main initialization routine
+ */
+extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+int __init tx4925ndfmc_init (void)
+{
+       struct nand_chip *this;
+       int err = 0;
+
+       /* Allocate memory for MTD device structure and private data */
+       tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+                               GFP_KERNEL);
+       if (!tx4925ndfmc_mtd) {
+               printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n");
+               err = -ENOMEM;
+               goto out;
+       }
+
+        tx4925ndfmc_device_setup();
+
+       /* io is indirect via a register so don't need to ioremap address */
+
+       /* Get pointer to private data */
+       this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]);
+
+       /* Initialize structures */
+       memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info));
+       memset((char *) this, 0, sizeof(struct nand_chip));
+
+       /* Link the private data with the MTD structure */
+       tx4925ndfmc_mtd->priv = this;
+
+       /* Set address of NAND IO lines */
+       this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr);
+       this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr);
+       this->hwcontrol = tx4925ndfmc_hwcontrol;
+       this->enable_hwecc = tx4925ndfmc_enable_hwecc;
+       this->calculate_ecc = tx4925ndfmc_readecc;
+       this->correct_data = nand_correct_data;
+       this->eccmode = NAND_ECC_HW6_512;       
+       this->dev_ready = tx4925ndfmc_device_ready;
+       /* 20 us command delay time */
+       this->chip_delay = 20;          
+        this->read_byte = tx4925ndfmc_nand_read_byte;
+        this->write_byte = tx4925ndfmc_nand_write_byte;
+       this->cmdfunc = tx4925ndfmc_nand_command;
+       this->write_buf = tx4925ndfmc_nand_write_buf;
+       this->read_buf = tx4925ndfmc_nand_read_buf;
+       this->verify_buf = tx4925ndfmc_nand_verify_buf;
+
+       /* Scan to find existance of the device */
+       if (nand_scan (tx4925ndfmc_mtd, 1)) {
+               err = -ENXIO;
+               goto out_ior;
+       }
+
+       /* Allocate memory for internal data buffer */
+       this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL);
+       if (!this->data_buf) {
+               printk ("Unable to allocate NAND data buffer for RBTX4925.\n");
+               err = -ENOMEM;
+               goto out_ior;
+       }
+
+       /* Register the partitions */
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+        {
+                int mtd_parts_nb = 0;
+                struct mtd_partition *mtd_parts = 0;
+                mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc");
+                if (mtd_parts_nb > 0)
+                        add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb);
+                else
+                        add_mtd_device(tx4925ndfmc_mtd);
+        }
+#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */
+       switch(tx4925ndfmc_mtd->size){
+               case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break;
+               case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break;
+               case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break; 
+               case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break; 
+               default: {
+                       printk ("Unsupported SmartMedia device\n"); 
+                       err = -ENXIO;
+                       goto out_buf;
+               }
+       }
+#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
+       goto out;
+
+out_buf:
+       kfree (this->data_buf);    
+out_ior:
+out:
+       return err;
+}
+
+module_init(tx4925ndfmc_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit tx4925ndfmc_cleanup (void)
+{
+       struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1];
+
+       /* Unregister partitions */
+       del_mtd_partitions(tx4925ndfmc_mtd);
+       
+       /* Unregister the device */
+       del_mtd_device (tx4925ndfmc_mtd);
+
+       /* Free internal data buffers */
+       kfree (this->data_buf);
+
+       /* Free the MTD device structure */
+       kfree (tx4925ndfmc_mtd);
+}
+module_exit(tx4925ndfmc_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
+MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925");
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
new file mode 100644 (file)
index 0000000..5005c1c
--- /dev/null
@@ -0,0 +1,1926 @@
+/* 
+ * drivers/net/gianfar.c
+ *
+ * Gianfar Ethernet Driver
+ * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ *  Gianfar:  AKA Lambda Draconis, "Dragon"
+ *  RA 11 31 24.2
+ *  Dec +69 19 52
+ *  V 3.84
+ *  B-V +1.62
+ *
+ *  Theory of operation
+ *  This driver is designed for the Triple-speed Ethernet
+ *  controllers on the Freescale 8540/8560 integrated processors,
+ *  as well as the Fast Ethernet Controller on the 8540.  
+ *  
+ *  The driver is initialized through OCP.  Structures which
+ *  define the configuration needed by the board are defined in a
+ *  board structure in arch/ppc/platforms (though I do not
+ *  discount the possibility that other architectures could one
+ *  day be supported.  One assumption the driver currently makes
+ *  is that the PHY is configured in such a way to advertise all
+ *  capabilities.  This is a sensible default, and on certain
+ *  PHYs, changing this default encounters substantial errata
+ *  issues.  Future versions may remove this requirement, but for
+ *  now, it is best for the firmware to ensure this is the case.
+ *
+ *  The Gianfar Ethernet Controller uses a ring of buffer
+ *  descriptors.  The beginning is indicated by a register
+ *  pointing to the physical address of the start of the ring. 
+ *  The end is determined by a "wrap" bit being set in the 
+ *  last descriptor of the ring.
+ *
+ *  When a packet is received, the RXF bit in the
+ *  IEVENT register is set, triggering an interrupt when the 
+ *  corresponding bit in the IMASK register is also set (if
+ *  interrupt coalescing is active, then the interrupt may not
+ *  happen immediately, but will wait until either a set number
+ *  of frames or amount of time have passed.).  In NAPI, the
+ *  interrupt handler will signal there is work to be done, and
+ *  exit.  Without NAPI, the packet(s) will be handled
+ *  immediately.  Both methods will start at the last known empty
+ *  descriptor, and process every subsequent descriptor until there 
+ *  are none left with data (NAPI will stop after a set number of
+ *  packets to give time to other tasks, but will eventually
+ *  process all the packets).  The data arrives inside a
+ *  pre-allocated skb, and so after the skb is passed up to the
+ *  stack, a new skb must be allocated, and the address field in
+ *  the buffer descriptor must be updated to indicate this new
+ *  skb.
+ *
+ *  When the kernel requests that a packet be transmitted, the
+ *  driver starts where it left off last time, and points the
+ *  descriptor at the buffer which was passed in.  The driver
+ *  then informs the DMA engine that there are packets ready to
+ *  be transmitted.  Once the controller is finished transmitting
+ *  the packet, an interrupt may be triggered (under the same
+ *  conditions as for reception, but depending on the TXF bit).
+ *  The driver then cleans up the buffer.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc32.h>
+
+#include "gianfar.h"
+#include "gianfar_phy.h"
+#ifdef CONFIG_NET_FASTROUTE
+#include <linux/if_arp.h>
+#include <net/ip.h>
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
+#define irqreturn_t void
+#define IRQ_HANDLED 
+#endif
+
+#define TX_TIMEOUT      (1*HZ)
+#define SKB_ALLOC_TIMEOUT 1000000
+#undef BRIEF_GFAR_ERRORS
+#define VERBOSE_GFAR_ERRORS
+
+#ifdef CONFIG_GFAR_NAPI
+#define RECEIVE(x) netif_receive_skb(x)
+#else
+#define RECEIVE(x) netif_rx(x)
+#endif
+
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, "
+char gfar_driver_name[] = "Gianfar Ethernet";
+char gfar_driver_version[] = "1.0";
+
+int startup_gfar(struct net_device *dev);
+static int gfar_enet_open(struct net_device *dev);
+static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void gfar_timeout(struct net_device *dev);
+static int gfar_close(struct net_device *dev);
+struct sk_buff *gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp);
+static struct net_device_stats *gfar_get_stats(struct net_device *dev);
+static int gfar_set_mac_address(struct net_device *dev);
+static int gfar_change_mtu(struct net_device *dev, int new_mtu);
+static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs);
+irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void gfar_phy_change(void *data);
+static void gfar_phy_timer(unsigned long data);
+static void adjust_link(struct net_device *dev);
+static void init_registers(struct net_device *dev);
+static int init_phy(struct net_device *dev);
+static int gfar_probe(struct ocp_device *ocpdev);
+static void gfar_remove(struct ocp_device *ocpdev);
+void free_skb_resources(struct gfar_private *priv);
+static void gfar_set_multi(struct net_device *dev);
+static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
+#ifdef CONFIG_GFAR_NAPI
+static int gfar_poll(struct net_device *dev, int *budget);
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst);
+#endif
+static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length);
+#ifdef CONFIG_GFAR_NAPI
+static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
+#else
+static int gfar_clean_rx_ring(struct net_device *dev);
+#endif
+static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
+
+extern struct ethtool_ops gfar_ethtool_ops;
+extern void gfar_gstrings_normon(struct net_device *dev, u32 stringset, 
+               u8 * buf);
+extern void gfar_fill_stats_normon(struct net_device *dev, 
+               struct ethtool_stats *dummy, u64 * buf);
+extern int gfar_stats_count_normon(struct net_device *dev);
+
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc");
+MODULE_DESCRIPTION("Gianfar Ethernet Driver");
+MODULE_LICENSE("GPL");
+
+/* Called by the ocp code to initialize device data structures
+ * required for bringing up the device
+ * returns 0 on success */
+static int gfar_probe(struct ocp_device *ocpdev)
+{
+       u32 tempval;
+       struct ocp_device *mdiodev;
+       struct net_device *dev = NULL;
+       struct gfar_private *priv = NULL;
+       struct ocp_gfar_data *einfo;
+       int idx;
+       int err = 0;
+       struct ethtool_ops *dev_ethtool_ops;
+
+       einfo = (struct ocp_gfar_data *) ocpdev->def->additions;
+
+       if (einfo == NULL) {
+               printk(KERN_ERR "gfar %d: Missing additional data!\n",
+                      ocpdev->def->index);
+
+               return -ENODEV;
+       }
+
+       /* get a pointer to the register memory which can
+        * configure the PHYs.  If it's different from this set,
+        * get the device which has those regs */
+       if ((einfo->phyregidx >= 0) && (einfo->phyregidx != ocpdev->def->index)) {
+               mdiodev = ocp_find_device(OCP_ANY_ID,
+                                         OCP_FUNC_GFAR, einfo->phyregidx);
+
+               /* If the device which holds the MDIO regs isn't
+                * up, wait for it to come up */
+               if (mdiodev == NULL)
+                       return -EAGAIN;
+       } else {
+               mdiodev = ocpdev;
+       }
+
+       /* Create an ethernet device instance */
+       dev = alloc_etherdev(sizeof (*priv));
+
+       if (dev == NULL)
+               return -ENOMEM;
+
+       priv = netdev_priv(dev);
+
+       /* Set the info in the priv to the current info */
+       priv->einfo = einfo;
+
+       /* get a pointer to the register memory */
+       priv->regs = (struct gfar *)
+           ioremap(ocpdev->def->paddr, sizeof (struct gfar));
+
+       if (priv->regs == NULL) {
+               err = -ENOMEM;
+               goto regs_fail;
+       }
+
+       /* Set the PHY base address */
+       priv->phyregs = (struct gfar *)
+           ioremap(mdiodev->def->paddr, sizeof (struct gfar));
+
+       if (priv->phyregs == NULL) {
+               err = -ENOMEM;
+               goto phy_regs_fail;
+       }
+
+       ocp_set_drvdata(ocpdev, dev);
+
+       /* Stop the DMA engine now, in case it was running before */
+       /* (The firmware could have used it, and left it running). */
+       /* To do this, we write Graceful Receive Stop and Graceful */
+       /* Transmit Stop, and then wait until the corresponding bits */
+       /* in IEVENT indicate the stops have completed. */
+       tempval = gfar_read(&priv->regs->dmactrl);
+       tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
+       gfar_write(&priv->regs->dmactrl, tempval);
+
+       tempval = gfar_read(&priv->regs->dmactrl);
+       tempval |= (DMACTRL_GRS | DMACTRL_GTS);
+       gfar_write(&priv->regs->dmactrl, tempval);
+
+       while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC)))
+               cpu_relax();
+
+       /* Reset MAC layer */
+       gfar_write(&priv->regs->maccfg1, MACCFG1_SOFT_RESET);
+
+       tempval = (MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
+       gfar_write(&priv->regs->maccfg1, tempval);
+
+       /* Initialize MACCFG2. */
+       gfar_write(&priv->regs->maccfg2, MACCFG2_INIT_SETTINGS);
+
+       /* Initialize ECNTRL */
+       gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
+
+       /* Copy the station address into the dev structure, */
+       /* and into the address registers MAC_STNADDR1,2. */
+       /* Backwards, because little endian MACs are dumb. */
+       /* Don't set the regs if the firmware already did */
+       memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN);
+
+       /* Set the dev->base_addr to the gfar reg region */
+       dev->base_addr = (unsigned long) (priv->regs);
+
+       SET_MODULE_OWNER(dev);
+
+       /* Fill in the dev structure */
+       dev->open = gfar_enet_open;
+       dev->hard_start_xmit = gfar_start_xmit;
+       dev->tx_timeout = gfar_timeout;
+       dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_GFAR_NAPI
+       dev->poll = gfar_poll;
+       dev->weight = GFAR_DEV_WEIGHT;
+#endif
+       dev->stop = gfar_close;
+       dev->get_stats = gfar_get_stats;
+       dev->change_mtu = gfar_change_mtu;
+       dev->mtu = 1500;
+       dev->set_multicast_list = gfar_set_multi;
+       dev->flags |= IFF_MULTICAST;
+
+       dev_ethtool_ops = 
+               (struct ethtool_ops *)kmalloc(sizeof(struct ethtool_ops), 
+                                             GFP_KERNEL);
+
+       if(dev_ethtool_ops == NULL) {
+               err = -ENOMEM;
+               goto ethtool_fail;
+       }
+
+       memcpy(dev_ethtool_ops, &gfar_ethtool_ops, sizeof(gfar_ethtool_ops));
+
+       /* If there is no RMON support in this device, we don't
+        * want to expose non-existant statistics */
+       if((priv->einfo->flags & GFAR_HAS_RMON) == 0) {
+               dev_ethtool_ops->get_strings = gfar_gstrings_normon;
+               dev_ethtool_ops->get_stats_count = gfar_stats_count_normon;
+               dev_ethtool_ops->get_ethtool_stats = gfar_fill_stats_normon;
+       }
+
+       if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0) {
+               dev_ethtool_ops->set_coalesce = NULL;
+               dev_ethtool_ops->get_coalesce = NULL;
+       }
+
+       dev->ethtool_ops = dev_ethtool_ops;
+
+#ifdef CONFIG_NET_FASTROUTE
+       dev->accept_fastpath = gfar_accept_fastpath;
+#endif
+
+       priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
+#ifdef CONFIG_GFAR_BUFSTASH
+       priv->rx_stash_size = STASH_LENGTH;
+#endif
+       priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
+       priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
+
+       /* Initially, coalescing is disabled */
+       priv->txcoalescing = 0;
+       priv->txcount = 0;
+       priv->txtime = 0;
+       priv->rxcoalescing = 0;
+       priv->rxcount = 0;
+       priv->rxtime = 0;
+
+       err = register_netdev(dev);
+
+       if (err) {
+               printk(KERN_ERR "%s: Cannot register net device, aborting.\n",
+                      dev->name);
+               goto register_fail;
+       }
+
+       /* Print out the device info */
+       printk(DEVICE_NAME, dev->name);
+       for (idx = 0; idx < 6; idx++)
+               printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':');
+       printk("\n");
+
+       /* Even more device info helps when determining which kernel */
+       /* provided which set of benchmarks.  Since this is global for all */
+       /* devices, we only print it once */
+#ifdef CONFIG_GFAR_NAPI
+       printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
+#else
+       printk(KERN_INFO "%s: Running with NAPI disabled\n", dev->name);
+#endif
+       printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n",
+              dev->name, priv->rx_ring_size, priv->tx_ring_size);
+
+       return 0;
+
+
+register_fail:
+       kfree(dev_ethtool_ops);
+ethtool_fail:
+       iounmap((void *) priv->phyregs);
+phy_regs_fail:
+       iounmap((void *) priv->regs);
+regs_fail:
+       free_netdev(dev);
+       return -ENOMEM;
+}
+
+static void gfar_remove(struct ocp_device *ocpdev)
+{
+       struct net_device *dev = ocp_get_drvdata(ocpdev);
+       struct gfar_private *priv = netdev_priv(dev);
+
+       ocp_set_drvdata(ocpdev, NULL);
+
+       kfree(dev->ethtool_ops);
+       iounmap((void *) priv->regs);
+       iounmap((void *) priv->phyregs);
+       free_netdev(dev);
+}
+
+/* Configure the PHY for dev.
+ * returns 0 if success.  -1 if failure
+ */
+static int init_phy(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+       struct phy_info *curphy;
+
+       priv->link = 1;
+       priv->oldlink = 0;
+       priv->oldspeed = 0;
+       priv->olddplx = -1;
+
+       /* get info for this PHY */
+       curphy = get_phy_info(dev);
+
+       if (curphy == NULL) {
+               printk(KERN_ERR "%s: No PHY found\n", dev->name);
+               return -1;
+       }
+
+       priv->phyinfo = curphy;
+
+       /* Run the commands which configure the PHY */
+       phy_run_commands(dev, curphy->config);
+
+       return 0;
+}
+
+static void init_registers(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+
+       /* Clear IEVENT */
+       gfar_write(&priv->regs->ievent, IEVENT_INIT_CLEAR);
+
+       /* Initialize IMASK */
+       gfar_write(&priv->regs->imask, IMASK_INIT_CLEAR);
+
+       /* Init hash registers to zero */
+       gfar_write(&priv->regs->iaddr0, 0);
+       gfar_write(&priv->regs->iaddr1, 0);
+       gfar_write(&priv->regs->iaddr2, 0);
+       gfar_write(&priv->regs->iaddr3, 0);
+       gfar_write(&priv->regs->iaddr4, 0);
+       gfar_write(&priv->regs->iaddr5, 0);
+       gfar_write(&priv->regs->iaddr6, 0);
+       gfar_write(&priv->regs->iaddr7, 0);
+
+       gfar_write(&priv->regs->gaddr0, 0);
+       gfar_write(&priv->regs->gaddr1, 0);
+       gfar_write(&priv->regs->gaddr2, 0);
+       gfar_write(&priv->regs->gaddr3, 0);
+       gfar_write(&priv->regs->gaddr4, 0);
+       gfar_write(&priv->regs->gaddr5, 0);
+       gfar_write(&priv->regs->gaddr6, 0);
+       gfar_write(&priv->regs->gaddr7, 0);
+
+       /* Zero out rctrl */
+       gfar_write(&priv->regs->rctrl, 0x00000000);
+
+       /* Zero out the rmon mib registers if it has them */
+       if (priv->einfo->flags & GFAR_HAS_RMON) {
+               memset((void *) &(priv->regs->rmon), 0,
+                      sizeof (struct rmon_mib));
+
+               /* Mask off the CAM interrupts */
+               gfar_write(&priv->regs->rmon.cam1, 0xffffffff);
+               gfar_write(&priv->regs->rmon.cam2, 0xffffffff);
+       }
+
+       /* Initialize the max receive buffer length */
+       gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
+
+#ifdef CONFIG_GFAR_BUFSTASH
+       /* If we are stashing buffers, we need to set the
+        * extraction length to the size of the buffer */
+       gfar_write(&priv->regs->attreli, priv->rx_stash_size << 16);
+#endif
+
+       /* Initialize the Minimum Frame Length Register */
+       gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS);
+
+       /* Setup Attributes so that snooping is on for rx */
+       gfar_write(&priv->regs->attr, ATTR_INIT_SETTINGS);
+       gfar_write(&priv->regs->attreli, ATTRELI_INIT_SETTINGS);
+
+       /* Assign the TBI an address which won't conflict with the PHYs */
+       gfar_write(&priv->regs->tbipa, TBIPA_VALUE);
+}
+
+void stop_gfar(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+       struct gfar *regs = priv->regs;
+       unsigned long flags;
+       u32 tempval;
+
+       /* Lock it down */
+       spin_lock_irqsave(&priv->lock, flags);
+
+       /* Tell the kernel the link is down */
+       priv->link = 0;
+       adjust_link(dev);
+
+       /* Mask all interrupts */
+       gfar_write(&regs->imask, IMASK_INIT_CLEAR);
+
+       /* Clear all interrupts */
+       gfar_write(&regs->ievent, IEVENT_INIT_CLEAR);
+
+       /* Stop the DMA, and wait for it to stop */
+       tempval = gfar_read(&priv->regs->dmactrl);
+       if ((tempval & (DMACTRL_GRS | DMACTRL_GTS))
+           != (DMACTRL_GRS | DMACTRL_GTS)) {
+               tempval |= (DMACTRL_GRS | DMACTRL_GTS);
+               gfar_write(&priv->regs->dmactrl, tempval);
+
+               while (!(gfar_read(&priv->regs->ievent) &
+                        (IEVENT_GRSC | IEVENT_GTSC)))
+                       cpu_relax();
+       }
+
+       /* Disable Rx and Tx */
+       tempval = gfar_read(&regs->maccfg1);
+       tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN);
+       gfar_write(&regs->maccfg1, tempval);
+
+       if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
+               phy_run_commands(dev, priv->phyinfo->shutdown);
+       }
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* Free the IRQs */
+       if (priv->einfo->flags & GFAR_HAS_MULTI_INTR) {
+               free_irq(priv->einfo->interruptError, dev);
+               free_irq(priv->einfo->interruptTransmit, dev);
+               free_irq(priv->einfo->interruptReceive, dev);
+       } else {
+               free_irq(priv->einfo->interruptTransmit, dev);
+       }
+
+       if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
+               free_irq(priv->einfo->interruptPHY, dev);
+       } else {
+               del_timer_sync(&priv->phy_info_timer);
+       }
+
+       free_skb_resources(priv);
+
+       dma_unmap_single(NULL, gfar_read(&regs->tbase),
+                       sizeof(struct txbd)*priv->tx_ring_size,
+                       DMA_BIDIRECTIONAL);
+       dma_unmap_single(NULL, gfar_read(&regs->rbase),
+                       sizeof(struct rxbd)*priv->rx_ring_size,
+                       DMA_BIDIRECTIONAL);
+
+       /* Free the buffer descriptors */
+       kfree(priv->tx_bd_base);
+}
+
+/* If there are any tx skbs or rx skbs still around, free them.
+ * Then free tx_skbuff and rx_skbuff */
+void free_skb_resources(struct gfar_private *priv)
+{
+       struct rxbd8 *rxbdp;
+       struct txbd8 *txbdp;
+       int i;
+
+       /* Go through all the buffer descriptors and free their data buffers */
+       txbdp = priv->tx_bd_base;
+
+       for (i = 0; i < priv->tx_ring_size; i++) {
+
+               if (priv->tx_skbuff[i]) {
+                       dma_unmap_single(NULL, txbdp->bufPtr,
+                                       txbdp->length,
+                                       DMA_TO_DEVICE);
+                       dev_kfree_skb_any(priv->tx_skbuff[i]);
+                       priv->tx_skbuff[i] = NULL;
+               }
+       }
+
+       kfree(priv->tx_skbuff);
+
+       rxbdp = priv->rx_bd_base;
+
+       /* rx_skbuff is not guaranteed to be allocated, so only
+        * free it and its contents if it is allocated */
+       if(priv->rx_skbuff != NULL) {
+               for (i = 0; i < priv->rx_ring_size; i++) {
+                       if (priv->rx_skbuff[i]) {
+                               dma_unmap_single(NULL, rxbdp->bufPtr,
+                                               priv->rx_buffer_size
+                                               + RXBUF_ALIGNMENT,
+                                               DMA_FROM_DEVICE);
+
+                               dev_kfree_skb_any(priv->rx_skbuff[i]);
+                               priv->rx_skbuff[i] = NULL;
+                       }
+
+                       rxbdp->status = 0;
+                       rxbdp->length = 0;
+                       rxbdp->bufPtr = 0;
+
+                       rxbdp++;
+               }
+
+               kfree(priv->rx_skbuff);
+       }
+}
+
+/* Bring the controller up and running */
+int startup_gfar(struct net_device *dev)
+{
+       struct txbd8 *txbdp;
+       struct rxbd8 *rxbdp;
+       unsigned long addr;
+       int i;
+       struct gfar_private *priv = netdev_priv(dev);
+       struct gfar *regs = priv->regs;
+       u32 tempval;
+       int err = 0;
+
+       gfar_write(&regs->imask, IMASK_INIT_CLEAR);
+
+       /* Allocate memory for the buffer descriptors */
+       addr =
+           (unsigned int) kmalloc(sizeof (struct txbd8) * priv->tx_ring_size +
+                                  sizeof (struct rxbd8) * priv->rx_ring_size,
+                                  GFP_KERNEL);
+
+       if (addr == 0) {
+               printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
+                      dev->name);
+               return -ENOMEM;
+       }
+
+       priv->tx_bd_base = (struct txbd8 *) addr;
+
+       /* enet DMA only understands physical addresses */
+       gfar_write(&regs->tbase, 
+                       dma_map_single(NULL, (void *)addr, 
+                               sizeof(struct txbd8) * priv->tx_ring_size,
+                               DMA_BIDIRECTIONAL));
+
+       /* Start the rx descriptor ring where the tx ring leaves off */
+       addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
+       priv->rx_bd_base = (struct rxbd8 *) addr;
+       gfar_write(&regs->rbase, 
+                       dma_map_single(NULL, (void *)addr, 
+                               sizeof(struct rxbd8) * priv->rx_ring_size,
+                               DMA_BIDIRECTIONAL));
+
+       /* Setup the skbuff rings */
+       priv->tx_skbuff =
+           (struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
+                                       priv->tx_ring_size, GFP_KERNEL);
+
+       if (priv->tx_skbuff == NULL) {
+               printk(KERN_ERR "%s: Could not allocate tx_skbuff\n",
+                      dev->name);
+               err = -ENOMEM;
+               goto tx_skb_fail;
+       }
+
+       for (i = 0; i < priv->tx_ring_size; i++)
+               priv->tx_skbuff[i] = NULL;
+
+       priv->rx_skbuff =
+           (struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
+                                       priv->rx_ring_size, GFP_KERNEL);
+
+       if (priv->rx_skbuff == NULL) {
+               printk(KERN_ERR "%s: Could not allocate rx_skbuff\n",
+                      dev->name);
+               err = -ENOMEM;
+               goto rx_skb_fail;
+       }
+
+       for (i = 0; i < priv->rx_ring_size; i++)
+               priv->rx_skbuff[i] = NULL;
+
+       /* Initialize some variables in our dev structure */
+       priv->dirty_tx = priv->cur_tx = priv->tx_bd_base;
+       priv->cur_rx = priv->rx_bd_base;
+       priv->skb_curtx = priv->skb_dirtytx = 0;
+       priv->skb_currx = 0;
+
+       /* Initialize Transmit Descriptor Ring */
+       txbdp = priv->tx_bd_base;
+       for (i = 0; i < priv->tx_ring_size; i++) {
+               txbdp->status = 0;
+               txbdp->length = 0;
+               txbdp->bufPtr = 0;
+               txbdp++;
+       }
+
+       /* Set the last descriptor in the ring to indicate wrap */
+       txbdp--;
+       txbdp->status |= TXBD_WRAP;
+
+       rxbdp = priv->rx_bd_base;
+       for (i = 0; i < priv->rx_ring_size; i++) {
+               struct sk_buff *skb = NULL;
+
+               rxbdp->status = 0;
+
+               skb = gfar_new_skb(dev, rxbdp);
+
+               priv->rx_skbuff[i] = skb;
+
+               rxbdp++;
+       }
+
+       /* Set the last descriptor in the ring to wrap */
+       rxbdp--;
+       rxbdp->status |= RXBD_WRAP;
+
+       /* If the device has multiple interrupts, register for
+        * them.  Otherwise, only register for the one */
+       if (priv->einfo->flags & GFAR_HAS_MULTI_INTR) {
+               /* Install our interrupt handlers for Error, 
+                * Transmit, and Receive */
+               if (request_irq(priv->einfo->interruptError, gfar_error,
+                               SA_SHIRQ, "enet_error", dev) < 0) {
+                       printk(KERN_ERR "%s: Can't get IRQ %d\n",
+                              dev->name, priv->einfo->interruptError);
+
+                       err = -1;
+                       goto err_irq_fail;
+               }
+
+               if (request_irq(priv->einfo->interruptTransmit, gfar_transmit,
+                               SA_SHIRQ, "enet_tx", dev) < 0) {
+                       printk(KERN_ERR "%s: Can't get IRQ %d\n",
+                              dev->name, priv->einfo->interruptTransmit);
+
+                       err = -1;
+
+                       goto tx_irq_fail;
+               }
+
+               if (request_irq(priv->einfo->interruptReceive, gfar_receive,
+                               SA_SHIRQ, "enet_rx", dev) < 0) {
+                       printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n",
+                              dev->name, priv->einfo->interruptReceive);
+
+                       err = -1;
+                       goto rx_irq_fail;
+               }
+       } else {
+               if (request_irq(priv->einfo->interruptTransmit, gfar_interrupt,
+                               SA_SHIRQ, "gfar_interrupt", dev) < 0) {
+                       printk(KERN_ERR "%s: Can't get IRQ %d\n",
+                              dev->name, priv->einfo->interruptError);
+
+                       err = -1;
+                       goto err_irq_fail;
+               }
+       }
+
+       /* Grab the PHY interrupt */
+       if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
+               if (request_irq(priv->einfo->interruptPHY, phy_interrupt,
+                               SA_SHIRQ, "phy_interrupt", dev) < 0) {
+                       printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+                              dev->name, priv->einfo->interruptPHY);
+
+                       err = -1;
+
+                       if (priv->einfo->flags & GFAR_HAS_MULTI_INTR)
+                               goto phy_irq_fail;
+                       else
+                               goto tx_irq_fail;
+               }
+       } else {
+               init_timer(&priv->phy_info_timer);
+               priv->phy_info_timer.function = &gfar_phy_timer;
+               priv->phy_info_timer.data = (unsigned long) dev;
+               mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
+       }
+
+       /* Set up the bottom half queue */
+       INIT_WORK(&priv->tq, (void (*)(void *))gfar_phy_change, dev);
+
+       /* Configure the PHY interrupt */
+       phy_run_commands(dev, priv->phyinfo->startup);
+
+       /* Tell the kernel the link is up, and determine the
+        * negotiated features (speed, duplex) */
+       adjust_link(dev);
+
+       if (priv->link == 0)
+               printk(KERN_INFO "%s: No link detected\n", dev->name);
+
+       /* Configure the coalescing support */
+       if (priv->txcoalescing)
+               gfar_write(&regs->txic,
+                          mk_ic_value(priv->txcount, priv->txtime));
+       else
+               gfar_write(&regs->txic, 0);
+
+       if (priv->rxcoalescing)
+               gfar_write(&regs->rxic,
+                          mk_ic_value(priv->rxcount, priv->rxtime));
+       else
+               gfar_write(&regs->rxic, 0);
+
+       init_waitqueue_head(&priv->rxcleanupq);
+
+       /* Enable Rx and Tx in MACCFG1 */
+       tempval = gfar_read(&regs->maccfg1);
+       tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
+       gfar_write(&regs->maccfg1, tempval);
+
+       /* Initialize DMACTRL to have WWR and WOP */
+       tempval = gfar_read(&priv->regs->dmactrl);
+       tempval |= DMACTRL_INIT_SETTINGS;
+       gfar_write(&priv->regs->dmactrl, tempval);
+
+       /* Clear THLT, so that the DMA starts polling now */
+       gfar_write(&regs->tstat, TSTAT_CLEAR_THALT);
+
+       /* Make sure we aren't stopped */
+       tempval = gfar_read(&priv->regs->dmactrl);
+       tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
+       gfar_write(&priv->regs->dmactrl, tempval);
+
+       /* Unmask the interrupts we look for */
+       gfar_write(&regs->imask, IMASK_DEFAULT);
+
+       return 0;
+
+phy_irq_fail:
+       free_irq(priv->einfo->interruptReceive, dev);
+rx_irq_fail:
+       free_irq(priv->einfo->interruptTransmit, dev);
+tx_irq_fail:
+       free_irq(priv->einfo->interruptError, dev);
+err_irq_fail:
+rx_skb_fail:
+       free_skb_resources(priv);
+tx_skb_fail:
+       kfree(priv->tx_bd_base);
+       return err;
+}
+
+/* Called when something needs to use the ethernet device */
+/* Returns 0 for success. */
+static int gfar_enet_open(struct net_device *dev)
+{
+       int err;
+
+       /* Initialize a bunch of registers */
+       init_registers(dev);
+
+       gfar_set_mac_address(dev);
+
+       err = init_phy(dev);
+
+       if (err)
+               return err;
+
+       err = startup_gfar(dev);
+
+       netif_start_queue(dev);
+
+       return err;
+}
+
+/* This is called by the kernel when a frame is ready for transmission. */
+/* It is pointed to by the dev->hard_start_xmit function pointer */
+static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+       struct txbd8 *txbdp;
+
+       /* Update transmit stats */
+       priv->stats.tx_bytes += skb->len;
+
+       /* Lock priv now */
+       spin_lock_irq(&priv->lock);
+
+       /* Point at the first free tx descriptor */
+       txbdp = priv->cur_tx;
+
+       /* Clear all but the WRAP status flags */
+       txbdp->status &= TXBD_WRAP;
+
+       /* Set buffer length and pointer */
+       txbdp->length = skb->len;
+       txbdp->bufPtr = dma_map_single(NULL, skb->data, 
+                       skb->len, DMA_TO_DEVICE);
+
+       /* Save the skb pointer so we can free it later */
+       priv->tx_skbuff[priv->skb_curtx] = skb;
+
+       /* Update the current skb pointer (wrapping if this was the last) */
+       priv->skb_curtx =
+           (priv->skb_curtx + 1) & TX_RING_MOD_MASK(priv->tx_ring_size);
+
+       /* Flag the BD as interrupt-causing */
+       txbdp->status |= TXBD_INTERRUPT;
+
+       /* Flag the BD as ready to go, last in frame, and  */
+       /* in need of CRC */
+       txbdp->status |= (TXBD_READY | TXBD_LAST | TXBD_CRC);
+
+       dev->trans_start = jiffies;
+
+       /* If this was the last BD in the ring, the next one */
+       /* is at the beginning of the ring */
+       if (txbdp->status & TXBD_WRAP)
+               txbdp = priv->tx_bd_base;
+       else
+               txbdp++;
+
+       /* If the next BD still needs to be cleaned up, then the bds
+          are full.  We need to tell the kernel to stop sending us stuff. */
+       if (txbdp == priv->dirty_tx) {
+               netif_stop_queue(dev);
+
+               priv->stats.tx_fifo_errors++;
+       }
+
+       /* Update the current txbd to the next one */
+       priv->cur_tx = txbdp;
+
+       /* Tell the DMA to go go go */
+       gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+
+       /* Unlock priv */
+       spin_unlock_irq(&priv->lock);
+
+       return 0;
+}
+
+/* Stops the kernel queue, and halts the controller */
+static int gfar_close(struct net_device *dev)
+{
+       stop_gfar(dev);
+
+       netif_stop_queue(dev);
+
+       return 0;
+}
+
+/* returns a net_device_stats structure pointer */
+static struct net_device_stats * gfar_get_stats(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+
+       return &(priv->stats);
+}
+
+/* Changes the mac address if the controller is not running. */
+int gfar_set_mac_address(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+       int i;
+       char tmpbuf[MAC_ADDR_LEN];
+       u32 tempval;
+
+       /* Now copy it into the mac registers backwards, cuz */
+       /* little endian is silly */
+       for (i = 0; i < MAC_ADDR_LEN; i++)
+               tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->dev_addr[i];
+
+       gfar_write(&priv->regs->macstnaddr1, *((u32 *) (tmpbuf)));
+
+       tempval = *((u32 *) (tmpbuf + 4));
+
+       gfar_write(&priv->regs->macstnaddr2, tempval);
+
+       return 0;
+}
+
+/**********************************************************************
+ * gfar_accept_fastpath
+ *
+ * Used to authenticate to the kernel that a fast path entry can be
+ * added to device's routing table cache
+ *
+ * Input : pointer to ethernet interface network device structure and
+ *         a pointer to the designated entry to be added to the cache.
+ * Output : zero upon success, negative upon failure
+ **********************************************************************/
+#ifdef CONFIG_NET_FASTROUTE
+static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
+{
+       struct net_device *odev = dst->dev;
+
+       if ((dst->ops->protocol != __constant_htons(ETH_P_IP))
+                       || (odev->type != ARPHRD_ETHER) 
+                       || (odev->accept_fastpath == NULL)) {
+               return -1;
+       }
+
+       return 0;
+}
+#endif
+
+/* try_fastroute() -- Checks the fastroute cache to see if a given packet
+ *   can be routed immediately to another device.  If it can, we send it.
+ *   If we used a fastroute, we return 1.  Otherwise, we return 0.
+ *   Returns 0 if CONFIG_NET_FASTROUTE is not on
+ */
+static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length)
+{
+#ifdef CONFIG_NET_FASTROUTE
+       struct ethhdr *eth;
+       struct iphdr *iph;
+       unsigned int hash;
+       struct rtable *rt;
+       struct net_device *odev;
+       struct gfar_private *priv = netdev_priv(dev);
+       unsigned int CPU_ID = smp_processor_id();
+
+       eth = (struct ethhdr *) (skb->data);
+
+       /* Only route ethernet IP packets */
+       if (eth->h_proto == __constant_htons(ETH_P_IP)) {
+               iph = (struct iphdr *) (skb->data + ETH_HLEN);
+
+               /* Generate the hash value */
+               hash = ((*(u8 *) &iph->daddr) ^ (*(u8 *) & iph->saddr)) & NETDEV_FASTROUTE_HMASK;
+
+               rt = (struct rtable *) (dev->fastpath[hash]);
+               if (rt != NULL 
+                               && ((*(u32 *) &iph->daddr) == (*(u32 *) &rt->key.dst))
+                               && ((*(u32 *) &iph->saddr) == (*(u32 *) &rt->key.src))
+                               && !(rt->u.dst.obsolete)) {
+                       odev = rt->u.dst.dev;
+                       netdev_rx_stat[CPU_ID].fastroute_hit++;
+
+                       /* Make sure the packet is:
+                        * 1) IPv4
+                        * 2) without any options (header length of 5)
+                        * 3) Not a multicast packet
+                        * 4) going to a valid destination
+                        * 5) Not out of time-to-live
+                        */
+                       if (iph->version == 4 
+                                       && iph->ihl == 5 
+                                       && (!(eth->h_dest[0] & 0x01)) 
+                                       && neigh_is_valid(rt->u.dst.neighbour) 
+                                       && iph->ttl > 1) {
+
+                               /* Fast Route Path: Taken if the outgoing device is ready to transmit the packet now */
+                               if ((!netif_queue_stopped(odev)) 
+                                               && (!spin_is_locked(odev->xmit_lock)) 
+                                               && (skb->len <= (odev->mtu + ETH_HLEN + 2 + 4))) {
+
+                                       skb->pkt_type = PACKET_FASTROUTE;
+                                       skb->protocol = __constant_htons(ETH_P_IP);
+                                       ip_decrease_ttl(iph);
+                                       memcpy(eth->h_source, odev->dev_addr, MAC_ADDR_LEN);
+                                       memcpy(eth->h_dest, rt->u.dst.neighbour->ha, MAC_ADDR_LEN);
+                                       skb->dev = odev;
+
+                                       /* Prep the skb for the packet */
+                                       skb_put(skb, length);
+
+                                       if (odev->hard_start_xmit(skb, odev) != 0) {
+                                               panic("%s: FastRoute path corrupted", dev->name);
+                                       }
+                                       netdev_rx_stat[CPU_ID].fastroute_success++;
+                               }
+
+                               /* Semi Fast Route Path: Mark the packet as needing fast routing, but let the
+                                * stack handle getting it to the device */
+                               else {
+                                       skb->pkt_type = PACKET_FASTROUTE;
+                                       skb->nh.raw = skb->data + ETH_HLEN;
+                                       skb->protocol = __constant_htons(ETH_P_IP);
+                                       netdev_rx_stat[CPU_ID].fastroute_defer++;
+
+                                       /* Prep the skb for the packet */
+                                       skb_put(skb, length);
+
+                                       if(RECEIVE(skb) == NET_RX_DROP) {
+                                               priv->extra_stats.kernel_dropped++;
+                                       }
+                               }
+
+                               return 1;
+                       }
+               }
+       }
+#endif /* CONFIG_NET_FASTROUTE */
+       return 0;
+}
+
+static int gfar_change_mtu(struct net_device *dev, int new_mtu)
+{
+       int tempsize, tempval;
+       struct gfar_private *priv = netdev_priv(dev);
+       int oldsize = priv->rx_buffer_size;
+       int frame_size = new_mtu + 18;
+
+       if ((frame_size < 64) || (frame_size > JUMBO_FRAME_SIZE)) {
+               printk(KERN_ERR "%s: Invalid MTU setting\n", dev->name);
+               return -EINVAL;
+       }
+
+       tempsize =
+           (frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) +
+           INCREMENTAL_BUFFER_SIZE;
+
+       /* Only stop and start the controller if it isn't already
+        * stopped */
+       if ((oldsize != tempsize) && (dev->flags & IFF_UP))
+               stop_gfar(dev);
+
+       priv->rx_buffer_size = tempsize;
+
+       dev->mtu = new_mtu;
+
+       gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
+       gfar_write(&priv->regs->maxfrm, priv->rx_buffer_size);
+
+       /* If the mtu is larger than the max size for standard
+        * ethernet frames (ie, a jumbo frame), then set maccfg2
+        * to allow huge frames, and to check the length */
+       tempval = gfar_read(&priv->regs->maccfg2);
+
+       if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE)
+               tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
+       else
+               tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
+
+       gfar_write(&priv->regs->maccfg2, tempval);
+
+       if ((oldsize != tempsize) && (dev->flags & IFF_UP))
+               startup_gfar(dev);
+
+       return 0;
+}
+
+/* gfar_timeout gets called when a packet has not been
+ * transmitted after a set amount of time.
+ * For now, assume that clearing out all the structures, and
+ * starting over will fix the problem. */
+static void gfar_timeout(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+
+       priv->stats.tx_errors++;
+
+       if (dev->flags & IFF_UP) {
+               stop_gfar(dev);
+               startup_gfar(dev);
+       }
+
+       if (!netif_queue_stopped(dev))
+               netif_schedule(dev);
+}
+
+/* Interrupt Handler for Transmit complete */
+static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = (struct net_device *) dev_id;
+       struct gfar_private *priv = netdev_priv(dev);
+       struct txbd8 *bdp;
+
+       /* Clear IEVENT */
+       gfar_write(&priv->regs->ievent, IEVENT_TX_MASK);
+
+       /* Lock priv */
+       spin_lock(&priv->lock);
+       bdp = priv->dirty_tx;
+       while ((bdp->status & TXBD_READY) == 0) {
+               /* If dirty_tx and cur_tx are the same, then either the */
+               /* ring is empty or full now (it could only be full in the beginning, */
+               /* obviously).  If it is empty, we are done. */
+               if ((bdp == priv->cur_tx) && (netif_queue_stopped(dev) == 0))
+                       break;
+
+               priv->stats.tx_packets++;
+
+               /* Deferred means some collisions occurred during transmit, */
+               /* but we eventually sent the packet. */
+               if (bdp->status & TXBD_DEF)
+                       priv->stats.collisions++;
+
+               /* Free the sk buffer associated with this TxBD */
+               dev_kfree_skb_irq(priv->tx_skbuff[priv->skb_dirtytx]);
+               priv->tx_skbuff[priv->skb_dirtytx] = NULL;
+               priv->skb_dirtytx =
+                   (priv->skb_dirtytx +
+                    1) & TX_RING_MOD_MASK(priv->tx_ring_size);
+
+               /* update bdp to point at next bd in the ring (wrapping if necessary) */
+               if (bdp->status & TXBD_WRAP)
+                       bdp = priv->tx_bd_base;
+               else
+                       bdp++;
+
+               /* Move dirty_tx to be the next bd */
+               priv->dirty_tx = bdp;
+
+               /* We freed a buffer, so now we can restart transmission */
+               if (netif_queue_stopped(dev))
+                       netif_wake_queue(dev);
+       } /* while ((bdp->status & TXBD_READY) == 0) */
+
+       /* If we are coalescing the interrupts, reset the timer */
+       /* Otherwise, clear it */
+       if (priv->txcoalescing)
+               gfar_write(&priv->regs->txic,
+                          mk_ic_value(priv->txcount, priv->txtime));
+       else
+               gfar_write(&priv->regs->txic, 0);
+
+       spin_unlock(&priv->lock);
+
+       return IRQ_HANDLED;
+}
+
+struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+       struct sk_buff *skb = NULL;
+       unsigned int timeout = SKB_ALLOC_TIMEOUT;
+
+       /* We have to allocate the skb, so keep trying till we succeed */
+       while ((!skb) && timeout--)
+               skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT);
+
+       if (skb == NULL)
+               return NULL;
+
+       /* We need the data buffer to be aligned properly.  We will reserve
+        * as many bytes as needed to align the data properly
+        */
+       skb_reserve(skb,
+                   RXBUF_ALIGNMENT -
+                   (((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1)));
+
+       skb->dev = dev;
+
+       bdp->bufPtr = dma_map_single(NULL, skb->data,
+                       priv->rx_buffer_size + RXBUF_ALIGNMENT, 
+                       DMA_FROM_DEVICE);
+
+       bdp->length = 0;
+
+       /* Mark the buffer empty */
+       bdp->status |= (RXBD_EMPTY | RXBD_INTERRUPT);
+
+       return skb;
+}
+
+static inline void count_errors(unsigned short status, struct gfar_private *priv)
+{
+       struct net_device_stats *stats = &priv->stats;
+       struct gfar_extra_stats *estats = &priv->extra_stats;
+
+       /* If the packet was truncated, none of the other errors
+        * matter */
+       if (status & RXBD_TRUNCATED) {
+               stats->rx_length_errors++;
+
+               estats->rx_trunc++;
+
+               return;
+       }
+       /* Count the errors, if there were any */
+       if (status & (RXBD_LARGE | RXBD_SHORT)) {
+               stats->rx_length_errors++;
+
+               if (status & RXBD_LARGE)
+                       estats->rx_large++;
+               else
+                       estats->rx_short++;
+       }
+       if (status & RXBD_NONOCTET) {
+               stats->rx_frame_errors++;
+               estats->rx_nonoctet++;
+       }
+       if (status & RXBD_CRCERR) {
+               estats->rx_crcerr++;
+               stats->rx_crc_errors++;
+       }
+       if (status & RXBD_OVERRUN) {
+               estats->rx_overrun++;
+               stats->rx_crc_errors++;
+       }
+}
+
+irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = (struct net_device *) dev_id;
+       struct gfar_private *priv = netdev_priv(dev);
+
+#ifdef CONFIG_GFAR_NAPI
+       u32 tempval;
+#endif
+
+       /* Clear IEVENT, so rx interrupt isn't called again
+        * because of this interrupt */
+       gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
+
+       /* support NAPI */
+#ifdef CONFIG_GFAR_NAPI
+       if (netif_rx_schedule_prep(dev)) {
+               tempval = gfar_read(&priv->regs->imask);
+               tempval &= IMASK_RX_DISABLED;
+               gfar_write(&priv->regs->imask, tempval);
+
+               __netif_rx_schedule(dev);
+       } else {
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: receive called twice (%x)[%x]\n",
+                      dev->name, gfar_read(priv->regs->ievent),
+                      gfar_read(priv->regs->imask));
+#endif
+       }
+#else
+
+       spin_lock(&priv->lock);
+       gfar_clean_rx_ring(dev);
+
+       /* If we are coalescing interrupts, update the timer */
+       /* Otherwise, clear it */
+       if (priv->rxcoalescing)
+               gfar_write(&priv->regs->rxic,
+                          mk_ic_value(priv->rxcount, priv->rxtime));
+       else
+               gfar_write(&priv->regs->rxic, 0);
+
+       /* Just in case we need to wake the ring param changer */
+       priv->rxclean = 1;
+
+       spin_unlock(&priv->lock);
+#endif
+
+       return IRQ_HANDLED;
+}
+
+
+/* gfar_process_frame() -- handle one incoming packet if skb
+ * isn't NULL.  Try the fastroute before using the stack */
+static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
+               int length)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+
+       if (skb == NULL) {
+#ifdef BRIEF_GFAR_ERRORS
+               printk(KERN_WARNING "%s: Missing skb!!.\n",
+                               dev->name);
+#endif
+               priv->stats.rx_dropped++;
+               priv->extra_stats.rx_skbmissing++;
+       } else {
+               if(try_fastroute(skb, dev, length) == 0) {
+                       /* Prep the skb for the packet */
+                       skb_put(skb, length);
+
+                       /* Tell the skb what kind of packet this is */
+                       skb->protocol = eth_type_trans(skb, dev);
+
+                       /* Send the packet up the stack */
+                       if (RECEIVE(skb) == NET_RX_DROP) {
+                               priv->extra_stats.kernel_dropped++;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/* gfar_clean_rx_ring() -- Processes each frame in the rx ring
+ *   until all are gone (or, in the case of NAPI, the budget/quota
+ *   has been reached). Returns the number of frames handled
+ */
+#ifdef CONFIG_GFAR_NAPI
+static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
+#else
+static int gfar_clean_rx_ring(struct net_device *dev)
+#endif
+{
+       struct rxbd8 *bdp;
+       struct sk_buff *skb;
+       u16 pkt_len;
+       int howmany = 0;
+       struct gfar_private *priv = netdev_priv(dev);
+
+       /* Get the first full descriptor */
+       bdp = priv->cur_rx;
+
+#ifdef CONFIG_GFAR_NAPI
+#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
+#else
+#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
+#endif
+       while (!GFAR_RXDONE()) {
+               skb = priv->rx_skbuff[priv->skb_currx];
+
+               if (!(bdp->status &
+                     (RXBD_LARGE | RXBD_SHORT | RXBD_NONOCTET
+                      | RXBD_CRCERR | RXBD_OVERRUN | RXBD_TRUNCATED))) {
+                       /* Increment the number of packets */
+                       priv->stats.rx_packets++;
+                       howmany++;
+
+                       /* Remove the FCS from the packet length */
+                       pkt_len = bdp->length - 4;
+
+                       gfar_process_frame(dev, skb, pkt_len);
+
+                       priv->stats.rx_bytes += pkt_len;
+
+               } else {
+                       count_errors(bdp->status, priv);
+
+                       if (skb)
+                               dev_kfree_skb_any(skb);
+
+                       priv->rx_skbuff[priv->skb_currx] = NULL;
+               }
+
+               dev->last_rx = jiffies;
+
+               /* Clear the status flags for this buffer */
+               bdp->status &= ~RXBD_STATS;
+
+               /* Add another skb for the future */
+               skb = gfar_new_skb(dev, bdp);
+               priv->rx_skbuff[priv->skb_currx] = skb;
+
+               /* Update to the next pointer */
+               if (bdp->status & RXBD_WRAP)
+                       bdp = priv->rx_bd_base;
+               else
+                       bdp++;
+
+               /* update to point at the next skb */
+               priv->skb_currx =
+                   (priv->skb_currx +
+                    1) & RX_RING_MOD_MASK(priv->rx_ring_size);
+
+       }
+
+       /* Update the current rxbd pointer to be the next one */
+       priv->cur_rx = bdp;
+
+       /* If no packets have arrived since the
+        * last one we processed, clear the IEVENT RX and
+        * BSY bits so that another interrupt won't be
+        * generated when we set IMASK */
+       if (bdp->status & RXBD_EMPTY)
+               gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
+
+       return howmany;
+}
+
+#ifdef CONFIG_GFAR_NAPI
+static int gfar_poll(struct net_device *dev, int *budget)
+{
+       int howmany;
+       struct gfar_private *priv = netdev_priv(dev);
+       int rx_work_limit = *budget;
+
+       if (rx_work_limit > dev->quota)
+               rx_work_limit = dev->quota;
+
+       spin_lock(&priv->lock);
+       howmany = gfar_clean_rx_ring(dev, rx_work_limit);
+
+       dev->quota -= howmany;
+       rx_work_limit -= howmany;
+       *budget -= howmany;
+
+       if (rx_work_limit >= 0) {
+               netif_rx_complete(dev);
+
+               /* Clear the halt bit in RSTAT */
+               gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
+
+               gfar_write(&priv->regs->imask, IMASK_DEFAULT);
+
+               /* If we are coalescing interrupts, update the timer */
+               /* Otherwise, clear it */
+               if (priv->rxcoalescing)
+                       gfar_write(&priv->regs->rxic,
+                                  mk_ic_value(priv->rxcount, priv->rxtime));
+               else
+                       gfar_write(&priv->regs->rxic, 0);
+
+               /* Signal to the ring size changer that it's safe to go */
+               priv->rxclean = 1;
+       }
+
+       spin_unlock(priv->lock);
+
+       return (rx_work_limit < 0) ? 1 : 0;
+}
+#endif
+
+/* The interrupt handler for devices with one interrupt */
+static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = dev_id;
+       struct gfar_private *priv = netdev_priv(dev);
+
+       /* Save ievent for future reference */
+       u32 events = gfar_read(&priv->regs->ievent);
+
+       /* Clear IEVENT */
+       gfar_write(&priv->regs->ievent, events);
+
+       /* Check for reception */
+       if ((events & IEVENT_RXF0) || (events & IEVENT_RXB0))
+               gfar_receive(irq, dev_id, regs);
+
+       /* Check for transmit completion */
+       if ((events & IEVENT_TXF) || (events & IEVENT_TXB))
+               gfar_transmit(irq, dev_id, regs);
+
+       /* Update error statistics */
+       if (events & IEVENT_TXE) {
+               priv->stats.tx_errors++;
+
+               if (events & IEVENT_LC)
+                       priv->stats.tx_window_errors++;
+               if (events & IEVENT_CRL)
+                       priv->stats.tx_aborted_errors++;
+               if (events & IEVENT_XFUN) {
+#ifdef VERBOSE_GFAR_ERRORS
+                       printk(KERN_WARNING "%s: tx underrun. dropped packet\n",
+                              dev->name);
+#endif
+                       priv->stats.tx_dropped++;
+                       priv->extra_stats.tx_underrun++;
+
+                       /* Reactivate the Tx Queues */
+                       gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+               }
+       }
+       if (events & IEVENT_BSY) {
+               priv->stats.rx_errors++;
+               priv->extra_stats.rx_bsy++;
+
+               gfar_receive(irq, dev_id, regs);
+
+#ifndef CONFIG_GFAR_NAPI
+               /* Clear the halt bit in RSTAT */
+               gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
+#endif
+
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", dev->name,
+                      gfar_read(priv->regs->rstat));
+#endif
+       }
+       if (events & IEVENT_BABR) {
+               priv->stats.rx_errors++;
+               priv->extra_stats.rx_babr++;
+
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: babbling error\n", dev->name);
+#endif
+       }
+       if (events & IEVENT_EBERR) {
+               priv->extra_stats.eberr++;
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: EBERR\n", dev->name);
+#endif
+       }
+       if (events & IEVENT_RXC) {
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: control frame\n", dev->name);
+#endif
+       }
+
+       if (events & IEVENT_BABT) {
+               priv->extra_stats.tx_babt++;
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: babt error\n", dev->name);
+#endif
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = (struct net_device *) dev_id;
+       struct gfar_private *priv = netdev_priv(dev);
+
+       /* Run the commands which acknowledge the interrupt */
+       phy_run_commands(dev, priv->phyinfo->ack_int);
+
+       /* Schedule the bottom half */
+       schedule_work(&priv->tq);
+
+       return IRQ_HANDLED;
+}
+
+/* Scheduled by the phy_interrupt/timer to handle PHY changes */
+static void gfar_phy_change(void *data)
+{
+       struct net_device *dev = (struct net_device *) data;
+       struct gfar_private *priv = netdev_priv(dev);
+       int timeout = HZ / 1000 + 1;
+
+       /* Delay to give the PHY a chance to change the
+        * register state */
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(timeout);
+
+       /* Run the commands which check the link state */
+       phy_run_commands(dev, priv->phyinfo->handle_int);
+
+       /* React to the change in state */
+       adjust_link(dev);
+}
+
+/* Called every so often on systems that don't interrupt
+ * the core for PHY changes */
+static void gfar_phy_timer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *) data;
+       struct gfar_private *priv = netdev_priv(dev);
+
+       schedule_work(&priv->tq);
+
+       mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
+}
+
+/* Called every time the controller might need to be made
+ * aware of new link state.  The PHY code conveys this
+ * information through variables in the priv structure, and this
+ * function converts those variables into the appropriate
+ * register values, and can bring down the device if needed.
+ */
+static void adjust_link(struct net_device *dev)
+{
+       struct gfar_private *priv = netdev_priv(dev);
+       struct gfar *regs = priv->regs;
+       u32 tempval;
+
+       if (priv->link) {
+               /* Now we make sure that we can be in full duplex mode.
+                * If not, we operate in half-duplex mode. */
+               if (priv->duplexity != priv->olddplx) {
+                       if (!(priv->duplexity)) {
+                               tempval = gfar_read(&regs->maccfg2);
+                               tempval &= ~(MACCFG2_FULL_DUPLEX);
+                               gfar_write(&regs->maccfg2, tempval);
+
+                               printk(KERN_INFO "%s: Half Duplex\n",
+                                      dev->name);
+                       } else {
+                               tempval = gfar_read(&regs->maccfg2);
+                               tempval |= MACCFG2_FULL_DUPLEX;
+                               gfar_write(&regs->maccfg2, tempval);
+
+                               printk(KERN_INFO "%s: Full Duplex\n",
+                                      dev->name);
+                       }
+
+                       priv->olddplx = priv->duplexity;
+               }
+
+               if (priv->speed != priv->oldspeed) {
+                       switch (priv->speed) {
+                       case 1000:
+                               tempval = gfar_read(&regs->maccfg2);
+                               tempval =
+                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
+                               gfar_write(&regs->maccfg2, tempval);
+                               break;
+                       case 100:
+                       case 10:
+                               tempval = gfar_read(&regs->maccfg2);
+                               tempval =
+                                   ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
+                               gfar_write(&regs->maccfg2, tempval);
+                               break;
+                       default:
+                               printk(KERN_WARNING
+                                      "%s: Ack!  Speed (%d) is not 10/100/1000!\n",
+                                      dev->name, priv->speed);
+                               break;
+                       }
+
+                       printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
+                              priv->speed);
+
+                       priv->oldspeed = priv->speed;
+               }
+
+               if (!priv->oldlink) {
+                       printk(KERN_INFO "%s: Link is up\n", dev->name);
+                       priv->oldlink = 1;
+                       netif_carrier_on(dev);
+                       netif_schedule(dev);
+               }
+       } else {
+               if (priv->oldlink) {
+                       printk(KERN_INFO "%s: Link is down\n", dev->name);
+                       priv->oldlink = 0;
+                       priv->oldspeed = 0;
+                       priv->olddplx = -1;
+                       netif_carrier_off(dev);
+               }
+       }
+
+#ifdef VERBOSE_GFAR_ERRORS
+       printk(KERN_INFO "%s: Link now %s; %dBT %s-duplex\n",
+              dev->name, priv->link ? "up" : "down", priv->speed, priv->duplexity ? "full" : "half");
+#endif
+}
+
+
+/* Update the hash table based on the current list of multicast
+ * addresses we subscribe to.  Also, change the promiscuity of
+ * the device based on the flags (this function is called
+ * whenever dev->flags is changed */
+static void gfar_set_multi(struct net_device *dev)
+{
+       struct dev_mc_list *mc_ptr;
+       struct gfar_private *priv = netdev_priv(dev);
+       struct gfar *regs = priv->regs;
+       u32 tempval;
+
+       if(dev->flags & IFF_PROMISC) {
+               printk(KERN_INFO "%s: Entering promiscuous mode.\n",
+                               dev->name);
+               /* Set RCTRL to PROM */
+               tempval = gfar_read(&regs->rctrl);
+               tempval |= RCTRL_PROM;
+               gfar_write(&regs->rctrl, tempval);
+       } else {
+               /* Set RCTRL to not PROM */
+               tempval = gfar_read(&regs->rctrl);
+               tempval &= ~(RCTRL_PROM);
+               gfar_write(&regs->rctrl, tempval);
+       }
+       
+       if(dev->flags & IFF_ALLMULTI) {
+               /* Set the hash to rx all multicast frames */
+               gfar_write(&regs->gaddr0, 0xffffffff);
+               gfar_write(&regs->gaddr1, 0xffffffff);
+               gfar_write(&regs->gaddr2, 0xffffffff);
+               gfar_write(&regs->gaddr3, 0xffffffff);
+               gfar_write(&regs->gaddr4, 0xffffffff);
+               gfar_write(&regs->gaddr5, 0xffffffff);
+               gfar_write(&regs->gaddr6, 0xffffffff);
+               gfar_write(&regs->gaddr7, 0xffffffff);
+       } else {
+               /* zero out the hash */
+               gfar_write(&regs->gaddr0, 0x0);
+               gfar_write(&regs->gaddr1, 0x0);
+               gfar_write(&regs->gaddr2, 0x0);
+               gfar_write(&regs->gaddr3, 0x0);
+               gfar_write(&regs->gaddr4, 0x0);
+               gfar_write(&regs->gaddr5, 0x0);
+               gfar_write(&regs->gaddr6, 0x0);
+               gfar_write(&regs->gaddr7, 0x0);
+
+               if(dev->mc_count == 0)
+                       return;
+
+               /* Parse the list, and set the appropriate bits */
+               for(mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) {
+                       gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr);
+               }
+       }
+
+       return;
+}
+
+/* Set the appropriate hash bit for the given addr */
+/* The algorithm works like so:
+ * 1) Take the Destination Address (ie the multicast address), and
+ * do a CRC on it (little endian), and reverse the bits of the
+ * result.
+ * 2) Use the 8 most significant bits as a hash into a 256-entry
+ * table.  The table is controlled through 8 32-bit registers:
+ * gaddr0-7.  gaddr0's MSB is entry 0, and gaddr7's LSB is
+ * gaddr7.  This means that the 3 most significant bits in the
+ * hash index which gaddr register to use, and the 5 other bits
+ * indicate which bit (assuming an IBM numbering scheme, which
+ * for PowerPC (tm) is usually the case) in the register holds
+ * the entry. */
+static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
+{
+       u32 tempval;
+       struct gfar_private *priv = netdev_priv(dev);
+       struct gfar *regs = priv->regs;
+       u32 *hash = &regs->gaddr0;
+       u32 result = ether_crc(MAC_ADDR_LEN, addr);
+       u8 whichreg = ((result >> 29) & 0x7);
+       u8 whichbit = ((result >> 24) & 0x1f);
+       u32 value = (1 << (31-whichbit));
+
+       tempval = gfar_read(&hash[whichreg]);
+       tempval |= value;
+       gfar_write(&hash[whichreg], tempval);
+
+       return;
+}
+
+/* GFAR error interrupt handler */
+static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = dev_id;
+       struct gfar_private *priv = netdev_priv(dev);
+
+       /* Save ievent for future reference */
+       u32 events = gfar_read(&priv->regs->ievent);
+
+       /* Clear IEVENT */
+       gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK);
+
+       /* Hmm... */
+#if defined (BRIEF_GFAR_ERRORS) || defined (VERBOSE_GFAR_ERRORS)
+       printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n",
+              dev->name, events, gfar_read(priv->regs->imask));
+#endif
+
+       /* Update the error counters */
+       if (events & IEVENT_TXE) {
+               priv->stats.tx_errors++;
+
+               if (events & IEVENT_LC)
+                       priv->stats.tx_window_errors++;
+               if (events & IEVENT_CRL)
+                       priv->stats.tx_aborted_errors++;
+               if (events & IEVENT_XFUN) {
+#ifdef VERBOSE_GFAR_ERRORS
+                       printk(KERN_DEBUG "%s: underrun.  packet dropped.\n",
+                              dev->name);
+#endif
+                       priv->stats.tx_dropped++;
+                       priv->extra_stats.tx_underrun++;
+
+                       /* Reactivate the Tx Queues */
+                       gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+               }
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
+#endif
+       }
+       if (events & IEVENT_BSY) {
+               priv->stats.rx_errors++;
+               priv->extra_stats.rx_bsy++;
+
+               gfar_receive(irq, dev_id, regs);
+
+#ifndef CONFIG_GFAR_NAPI
+               /* Clear the halt bit in RSTAT */
+               gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
+#endif
+
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", dev->name,
+                      gfar_read(priv->regs->rstat));
+#endif
+       }
+       if (events & IEVENT_BABR) {
+               priv->stats.rx_errors++;
+               priv->extra_stats.rx_babr++;
+
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: babbling error\n", dev->name);
+#endif
+       }
+       if (events & IEVENT_EBERR) {
+               priv->extra_stats.eberr++;
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: EBERR\n", dev->name);
+#endif
+       }
+       if (events & IEVENT_RXC)
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: control frame\n", dev->name);
+#endif
+
+       if (events & IEVENT_BABT) {
+               priv->extra_stats.tx_babt++;
+#ifdef VERBOSE_GFAR_ERRORS
+               printk(KERN_DEBUG "%s: babt error\n", dev->name);
+#endif
+       }
+       return IRQ_HANDLED;
+}
+
+/* Structure for a device driver */
+static struct ocp_device_id gfar_ids[] = {
+       {.vendor = OCP_ANY_ID,.function = OCP_FUNC_GFAR},
+       {.vendor = OCP_VENDOR_INVALID}
+};
+
+static struct ocp_driver gfar_driver = {
+       .name = "gianfar",
+       .id_table = gfar_ids,
+
+       .probe = gfar_probe,
+       .remove = gfar_remove,
+};
+
+static int __init gfar_init(void)
+{
+       int rc;
+
+       rc = ocp_register_driver(&gfar_driver);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
+       if (rc != 0) {
+#else
+       if (rc == 0) {
+#endif
+               ocp_unregister_driver(&gfar_driver);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __exit gfar_exit(void)
+{
+       ocp_unregister_driver(&gfar_driver);
+}
+
+module_init(gfar_init);
+module_exit(gfar_exit);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
new file mode 100644 (file)
index 0000000..f7af346
--- /dev/null
@@ -0,0 +1,537 @@
+/* 
+ * drivers/net/gianfar.h
+ *
+ * Gianfar Ethernet Driver
+ * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ *  Still left to do:
+ *      -Add support for module parameters
+ */
+#ifndef __GIANFAR_H
+#define __GIANFAR_H
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/crc32.h>
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
+#include <linux/workqueue.h>
+#else
+#include <linux/tqueue.h>
+#define work_struct tq_struct
+#define schedule_work schedule_task
+#endif
+
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <asm/ocp.h>
+#include "gianfar_phy.h"
+
+/* The maximum number of packets to be handled in one call of gfar_poll */
+#define GFAR_DEV_WEIGHT 64
+
+/* Number of bytes to align the rx bufs to */
+#define RXBUF_ALIGNMENT 64
+
+/* The number of bytes which composes a unit for the purpose of
+ * allocating data buffers.  ie-for any given MTU, the data buffer
+ * will be the next highest multiple of 512 bytes. */
+#define INCREMENTAL_BUFFER_SIZE 512
+
+
+#define MAC_ADDR_LEN 6
+
+extern char gfar_driver_name[];
+extern char gfar_driver_version[];
+
+/* These need to be powers of 2 for this driver */
+#ifdef CONFIG_GFAR_NAPI
+#define DEFAULT_TX_RING_SIZE   256
+#define DEFAULT_RX_RING_SIZE   256
+#else
+#define DEFAULT_TX_RING_SIZE    64
+#define DEFAULT_RX_RING_SIZE    64
+#endif
+
+#define GFAR_RX_MAX_RING_SIZE   256
+#define GFAR_TX_MAX_RING_SIZE   256
+
+#define DEFAULT_RX_BUFFER_SIZE  1536
+#define TX_RING_MOD_MASK(size) (size-1)
+#define RX_RING_MOD_MASK(size) (size-1)
+#define JUMBO_BUFFER_SIZE 9728
+#define JUMBO_FRAME_SIZE 9600
+
+/* Latency of interface clock in nanoseconds */
+/* Interface clock latency , in this case, means the 
+ * time described by a value of 1 in the interrupt
+ * coalescing registers' time fields.  Since those fields
+ * refer to the time it takes for 64 clocks to pass, the
+ * latencies are as such:
+ * GBIT = 125MHz => 8ns/clock => 8*64 ns / tick
+ * 100 = 25 MHz => 40ns/clock => 40*64 ns / tick
+ * 10 = 2.5 MHz => 400ns/clock => 400*64 ns / tick
+ */
+#define GFAR_GBIT_TIME  512
+#define GFAR_100_TIME   2560
+#define GFAR_10_TIME    25600
+
+#define DEFAULT_TXCOUNT        16
+#define DEFAULT_TXTIME 32768
+
+#define DEFAULT_RXCOUNT        16
+#define DEFAULT_RXTIME 32768
+
+#define TBIPA_VALUE            0x1f
+#define MIIMCFG_INIT_VALUE     0x00000007
+#define MIIMCFG_RESET           0x80000000
+#define MIIMIND_BUSY            0x00000001
+
+/* MAC register bits */
+#define MACCFG1_SOFT_RESET     0x80000000
+#define MACCFG1_RESET_RX_MC    0x00080000
+#define MACCFG1_RESET_TX_MC    0x00040000
+#define MACCFG1_RESET_RX_FUN   0x00020000
+#define        MACCFG1_RESET_TX_FUN    0x00010000
+#define MACCFG1_LOOPBACK       0x00000100
+#define MACCFG1_RX_FLOW                0x00000020
+#define MACCFG1_TX_FLOW                0x00000010
+#define MACCFG1_SYNCD_RX_EN    0x00000008
+#define MACCFG1_RX_EN          0x00000004
+#define MACCFG1_SYNCD_TX_EN    0x00000002
+#define MACCFG1_TX_EN          0x00000001
+
+#define MACCFG2_INIT_SETTINGS  0x00007205
+#define MACCFG2_FULL_DUPLEX    0x00000001
+#define MACCFG2_IF              0x00000300
+#define MACCFG2_MII             0x00000100
+#define MACCFG2_GMII            0x00000200
+#define MACCFG2_HUGEFRAME      0x00000020
+#define MACCFG2_LENGTHCHECK    0x00000010
+
+#define ECNTRL_INIT_SETTINGS   0x00001000
+#define ECNTRL_TBI_MODE         0x00000020
+
+#define MRBLR_INIT_SETTINGS    DEFAULT_RX_BUFFER_SIZE
+
+#define MINFLR_INIT_SETTINGS   0x00000040
+
+/* Init to do tx snooping for buffers and descriptors */
+#define DMACTRL_INIT_SETTINGS   0x000000c3
+#define DMACTRL_GRS             0x00000010
+#define DMACTRL_GTS             0x00000008
+
+#define TSTAT_CLEAR_THALT       0x80000000
+
+/* Interrupt coalescing macros */
+#define IC_ICEN                        0x80000000
+#define IC_ICFT_MASK           0x1fe00000
+#define IC_ICFT_SHIFT          21
+#define mk_ic_icft(x)          \
+       (((unsigned int)x << IC_ICFT_SHIFT)&IC_ICFT_MASK)
+#define IC_ICTT_MASK           0x0000ffff
+#define mk_ic_ictt(x)          (x&IC_ICTT_MASK)
+
+#define mk_ic_value(count, time) (IC_ICEN | \
+                               mk_ic_icft(count) | \
+                               mk_ic_ictt(time))
+
+#define RCTRL_PROM             0x00000008
+#define RSTAT_CLEAR_RHALT       0x00800000
+
+#define IEVENT_INIT_CLEAR      0xffffffff
+#define IEVENT_BABR            0x80000000
+#define IEVENT_RXC             0x40000000
+#define IEVENT_BSY             0x20000000
+#define IEVENT_EBERR           0x10000000
+#define IEVENT_MSRO            0x04000000
+#define IEVENT_GTSC            0x02000000
+#define IEVENT_BABT            0x01000000
+#define IEVENT_TXC             0x00800000
+#define IEVENT_TXE             0x00400000
+#define IEVENT_TXB             0x00200000
+#define IEVENT_TXF             0x00100000
+#define IEVENT_LC              0x00040000
+#define IEVENT_CRL             0x00020000
+#define IEVENT_XFUN            0x00010000
+#define IEVENT_RXB0            0x00008000
+#define IEVENT_GRSC            0x00000100
+#define IEVENT_RXF0            0x00000080
+#define IEVENT_RX_MASK          (IEVENT_RXB0 | IEVENT_RXF0)
+#define IEVENT_TX_MASK          (IEVENT_TXB | IEVENT_TXF)
+#define IEVENT_ERR_MASK         \
+(IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \
+ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \
+ | IEVENT_CRL | IEVENT_XFUN)
+
+#define IMASK_INIT_CLEAR       0x00000000
+#define IMASK_BABR              0x80000000
+#define IMASK_RXC               0x40000000
+#define IMASK_BSY               0x20000000
+#define IMASK_EBERR             0x10000000
+#define IMASK_MSRO             0x04000000
+#define IMASK_GRSC              0x02000000
+#define IMASK_BABT             0x01000000
+#define IMASK_TXC               0x00800000
+#define IMASK_TXEEN            0x00400000
+#define IMASK_TXBEN            0x00200000
+#define IMASK_TXFEN             0x00100000
+#define IMASK_LC               0x00040000
+#define IMASK_CRL              0x00020000
+#define IMASK_XFUN             0x00010000
+#define IMASK_RXB0              0x00008000
+#define IMASK_GTSC              0x00000100
+#define IMASK_RXFEN0           0x00000080
+#define IMASK_RX_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY)
+#define IMASK_DEFAULT  (IMASK_TXEEN | IMASK_TXFEN | IMASK_TXBEN | \
+               IMASK_RXFEN0 | IMASK_BSY | IMASK_EBERR | IMASK_BABR | \
+               IMASK_XFUN | IMASK_RXC | IMASK_BABT)
+
+
+/* Attribute fields */
+
+/* This enables rx snooping for buffers and descriptors */
+#ifdef CONFIG_GFAR_BDSTASH
+#define ATTR_BDSTASH           0x00000800
+#else
+#define ATTR_BDSTASH           0x00000000
+#endif
+
+#ifdef CONFIG_GFAR_BUFSTASH
+#define ATTR_BUFSTASH          0x00004000
+#define STASH_LENGTH           64
+#else
+#define ATTR_BUFSTASH          0x00000000
+#endif
+
+#define ATTR_SNOOPING          0x000000c0
+#define ATTR_INIT_SETTINGS      (ATTR_SNOOPING \
+               | ATTR_BDSTASH | ATTR_BUFSTASH)
+
+#define ATTRELI_INIT_SETTINGS   0x0
+
+
+/* TxBD status field bits */
+#define TXBD_READY             0x8000
+#define TXBD_PADCRC            0x4000
+#define TXBD_WRAP              0x2000
+#define TXBD_INTERRUPT         0x1000
+#define TXBD_LAST              0x0800
+#define TXBD_CRC               0x0400
+#define TXBD_DEF               0x0200
+#define TXBD_HUGEFRAME         0x0080
+#define TXBD_LATECOLLISION     0x0080
+#define TXBD_RETRYLIMIT                0x0040
+#define        TXBD_RETRYCOUNTMASK     0x003c
+#define TXBD_UNDERRUN          0x0002
+
+/* RxBD status field bits */
+#define RXBD_EMPTY             0x8000
+#define RXBD_RO1               0x4000
+#define RXBD_WRAP              0x2000
+#define RXBD_INTERRUPT         0x1000
+#define RXBD_LAST              0x0800
+#define RXBD_FIRST             0x0400
+#define RXBD_MISS              0x0100
+#define RXBD_BROADCAST         0x0080
+#define RXBD_MULTICAST         0x0040
+#define RXBD_LARGE             0x0020
+#define RXBD_NONOCTET          0x0010
+#define RXBD_SHORT             0x0008
+#define RXBD_CRCERR            0x0004
+#define RXBD_OVERRUN           0x0002
+#define RXBD_TRUNCATED         0x0001
+#define RXBD_STATS             0x01ff
+
+struct txbd8
+{
+       u16     status; /* Status Fields */
+       u16     length; /* Buffer length */
+       u32     bufPtr; /* Buffer Pointer */
+};
+
+struct rxbd8
+{
+       u16     status; /* Status Fields */
+       u16     length; /* Buffer Length */
+       u32     bufPtr; /* Buffer Pointer */
+};
+
+struct rmon_mib
+{
+       u32     tr64;   /* 0x.680 - Transmit and Receive 64-byte Frame Counter */
+       u32     tr127;  /* 0x.684 - Transmit and Receive 65-127 byte Frame Counter */
+       u32     tr255;  /* 0x.688 - Transmit and Receive 128-255 byte Frame Counter */
+       u32     tr511;  /* 0x.68c - Transmit and Receive 256-511 byte Frame Counter */
+       u32     tr1k;   /* 0x.690 - Transmit and Receive 512-1023 byte Frame Counter */
+       u32     trmax;  /* 0x.694 - Transmit and Receive 1024-1518 byte Frame Counter */
+       u32     trmgv;  /* 0x.698 - Transmit and Receive 1519-1522 byte Good VLAN Frame */
+       u32     rbyt;   /* 0x.69c - Receive Byte Counter */
+       u32     rpkt;   /* 0x.6a0 - Receive Packet Counter */
+       u32     rfcs;   /* 0x.6a4 - Receive FCS Error Counter */
+       u32     rmca;   /* 0x.6a8 - Receive Multicast Packet Counter */
+       u32     rbca;   /* 0x.6ac - Receive Broadcast Packet Counter */
+       u32     rxcf;   /* 0x.6b0 - Receive Control Frame Packet Counter */
+       u32     rxpf;   /* 0x.6b4 - Receive Pause Frame Packet Counter */
+       u32     rxuo;   /* 0x.6b8 - Receive Unknown OP Code Counter */
+       u32     raln;   /* 0x.6bc - Receive Alignment Error Counter */
+       u32     rflr;   /* 0x.6c0 - Receive Frame Length Error Counter */
+       u32     rcde;   /* 0x.6c4 - Receive Code Error Counter */
+       u32     rcse;   /* 0x.6c8 - Receive Carrier Sense Error Counter */
+       u32     rund;   /* 0x.6cc - Receive Undersize Packet Counter */
+       u32     rovr;   /* 0x.6d0 - Receive Oversize Packet Counter */
+       u32     rfrg;   /* 0x.6d4 - Receive Fragments Counter */
+       u32     rjbr;   /* 0x.6d8 - Receive Jabber Counter */
+       u32     rdrp;   /* 0x.6dc - Receive Drop Counter */
+       u32     tbyt;   /* 0x.6e0 - Transmit Byte Counter Counter */
+       u32     tpkt;   /* 0x.6e4 - Transmit Packet Counter */
+       u32     tmca;   /* 0x.6e8 - Transmit Multicast Packet Counter */
+       u32     tbca;   /* 0x.6ec - Transmit Broadcast Packet Counter */
+       u32     txpf;   /* 0x.6f0 - Transmit Pause Control Frame Counter */
+       u32     tdfr;   /* 0x.6f4 - Transmit Deferral Packet Counter */
+       u32     tedf;   /* 0x.6f8 - Transmit Excessive Deferral Packet Counter */
+       u32     tscl;   /* 0x.6fc - Transmit Single Collision Packet Counter */
+       u32     tmcl;   /* 0x.700 - Transmit Multiple Collision Packet Counter */
+       u32     tlcl;   /* 0x.704 - Transmit Late Collision Packet Counter */
+       u32     txcl;   /* 0x.708 - Transmit Excessive Collision Packet Counter */
+       u32     tncl;   /* 0x.70c - Transmit Total Collision Counter */
+       u8      res1[4];
+       u32     tdrp;   /* 0x.714 - Transmit Drop Frame Counter */
+       u32     tjbr;   /* 0x.718 - Transmit Jabber Frame Counter */
+       u32     tfcs;   /* 0x.71c - Transmit FCS Error Counter */
+       u32     txcf;   /* 0x.720 - Transmit Control Frame Counter */
+       u32     tovr;   /* 0x.724 - Transmit Oversize Frame Counter */
+       u32     tund;   /* 0x.728 - Transmit Undersize Frame Counter */
+       u32     tfrg;   /* 0x.72c - Transmit Fragments Frame Counter */
+       u32     car1;   /* 0x.730 - Carry Register One */
+       u32     car2;   /* 0x.734 - Carry Register Two */
+       u32     cam1;   /* 0x.738 - Carry Mask Register One */
+       u32     cam2;   /* 0x.73c - Carry Mask Register Two */
+};
+
+struct gfar_extra_stats {
+       u64 kernel_dropped;
+       u64 rx_large;
+       u64 rx_short;
+       u64 rx_nonoctet;
+       u64 rx_crcerr;
+       u64 rx_overrun;
+       u64 rx_bsy;
+       u64 rx_babr;
+       u64 rx_trunc;
+       u64 eberr;
+       u64 tx_babt;
+       u64 tx_underrun;
+       u64 rx_skbmissing;
+       u64 tx_timeout;
+};
+
+#define GFAR_RMON_LEN ((sizeof(struct rmon_mib) - 16)/sizeof(u32))
+#define GFAR_EXTRA_STATS_LEN (sizeof(struct gfar_extra_stats)/sizeof(u64))
+
+/* Number of stats in the stats structure (ignore car and cam regs)*/
+#define GFAR_STATS_LEN (GFAR_RMON_LEN + GFAR_EXTRA_STATS_LEN)
+
+#define GFAR_INFOSTR_LEN 32
+
+struct gfar_stats {
+       u64 extra[GFAR_EXTRA_STATS_LEN];
+       u64 rmon[GFAR_RMON_LEN];
+};
+
+
+struct gfar {
+       u8      res1[16];
+       u32     ievent;                 /* 0x.010 - Interrupt Event Register */
+       u32     imask;                  /* 0x.014 - Interrupt Mask Register */
+       u32     edis;                   /* 0x.018 - Error Disabled Register */
+       u8      res2[4];
+       u32     ecntrl;                 /* 0x.020 - Ethernet Control Register */
+       u32     minflr;                 /* 0x.024 - Minimum Frame Length Register */
+       u32     ptv;                    /* 0x.028 - Pause Time Value Register */
+       u32     dmactrl;                /* 0x.02c - DMA Control Register */
+       u32     tbipa;                  /* 0x.030 - TBI PHY Address Register */
+       u8      res3[88];
+       u32     fifo_tx_thr;            /* 0x.08c - FIFO transmit threshold register */
+       u8      res4[8];
+       u32     fifo_tx_starve;         /* 0x.098 - FIFO transmit starve register */
+       u32     fifo_tx_starve_shutoff; /* 0x.09c - FIFO transmit starve shutoff register */
+       u8      res5[96];
+       u32     tctrl;                  /* 0x.100 - Transmit Control Register */
+       u32     tstat;                  /* 0x.104 - Transmit Status Register */
+       u8      res6[4];
+       u32     tbdlen;                 /* 0x.10c - Transmit Buffer Descriptor Data Length Register */
+       u32     txic;                   /* 0x.110 - Transmit Interrupt Coalescing Configuration Register */
+       u8      res7[16];
+       u32     ctbptr;                 /* 0x.124 - Current Transmit Buffer Descriptor Pointer Register */
+       u8      res8[92];
+       u32     tbptr;                  /* 0x.184 - Transmit Buffer Descriptor Pointer Low Register */
+       u8      res9[124];
+       u32     tbase;                  /* 0x.204 - Transmit Descriptor Base Address Register */
+       u8      res10[168];
+       u32     ostbd;                  /* 0x.2b0 - Out-of-Sequence Transmit Buffer Descriptor Register */
+       u32     ostbdp;                 /* 0x.2b4 - Out-of-Sequence Transmit Data Buffer Pointer Register */
+       u8      res11[72];
+       u32     rctrl;                  /* 0x.300 - Receive Control Register */
+       u32     rstat;                  /* 0x.304 - Receive Status Register */
+       u8      res12[4];
+       u32     rbdlen;                 /* 0x.30c - RxBD Data Length Register */
+       u32     rxic;                   /* 0x.310 - Receive Interrupt Coalescing Configuration Register */
+       u8      res13[16];
+       u32     crbptr;                 /* 0x.324 - Current Receive Buffer Descriptor Pointer */
+       u8      res14[24];
+       u32     mrblr;                  /* 0x.340 - Maximum Receive Buffer Length Register */
+       u8      res15[64];
+       u32     rbptr;                  /* 0x.384 - Receive Buffer Descriptor Pointer */
+       u8      res16[124];
+       u32     rbase;                  /* 0x.404 - Receive Descriptor Base Address */
+       u8      res17[248];
+       u32     maccfg1;                /* 0x.500 - MAC Configuration 1 Register */
+       u32     maccfg2;                /* 0x.504 - MAC Configuration 2 Register */
+       u32     ipgifg;                 /* 0x.508 - Inter Packet Gap/Inter Frame Gap Register */
+       u32     hafdup;                 /* 0x.50c - Half Duplex Register */
+       u32     maxfrm;                 /* 0x.510 - Maximum Frame Length Register */
+       u8      res18[12];
+       u32     miimcfg;                /* 0x.520 - MII Management Configuration Register */
+       u32     miimcom;                /* 0x.524 - MII Management Command Register */
+       u32     miimadd;                /* 0x.528 - MII Management Address Register */
+       u32     miimcon;                /* 0x.52c - MII Management Control Register */
+       u32     miimstat;               /* 0x.530 - MII Management Status Register */
+       u32     miimind;                /* 0x.534 - MII Management Indicator Register */
+       u8      res19[4];
+       u32     ifstat;                 /* 0x.53c - Interface Status Register */
+       u32     macstnaddr1;            /* 0x.540 - Station Address Part 1 Register */
+       u32     macstnaddr2;            /* 0x.544 - Station Address Part 2 Register */
+       u8      res20[312];
+       struct rmon_mib rmon;
+       u8      res21[192];
+       u32     iaddr0;                 /* 0x.800 - Indivdual address register 0 */
+       u32     iaddr1;                 /* 0x.804 - Indivdual address register 1 */
+       u32     iaddr2;                 /* 0x.808 - Indivdual address register 2 */
+       u32     iaddr3;                 /* 0x.80c - Indivdual address register 3 */
+       u32     iaddr4;                 /* 0x.810 - Indivdual address register 4 */
+       u32     iaddr5;                 /* 0x.814 - Indivdual address register 5 */
+       u32     iaddr6;                 /* 0x.818 - Indivdual address register 6 */
+       u32     iaddr7;                 /* 0x.81c - Indivdual address register 7 */
+       u8      res22[96];
+       u32     gaddr0;                 /* 0x.880 - Global address register 0 */
+       u32     gaddr1;                 /* 0x.884 - Global address register 1 */
+       u32     gaddr2;                 /* 0x.888 - Global address register 2 */
+       u32     gaddr3;                 /* 0x.88c - Global address register 3 */
+       u32     gaddr4;                 /* 0x.890 - Global address register 4 */
+       u32     gaddr5;                 /* 0x.894 - Global address register 5 */
+       u32     gaddr6;                 /* 0x.898 - Global address register 6 */
+       u32     gaddr7;                 /* 0x.89c - Global address register 7 */
+       u8      res23[856];
+       u32     attr;                   /* 0x.bf8 - Attributes Register */
+       u32     attreli;                /* 0x.bfc - Attributes Extract Length and Extract Index Register */
+       u8      res24[1024];
+
+};
+
+/* Struct stolen almost completely (and shamelessly) from the FCC enet source
+ * (Ok, that's not so true anymore, but there is a family resemblence)
+ * The GFAR buffer descriptors track the ring buffers.  The rx_bd_base
+ * and tx_bd_base always point to the currently available buffer.
+ * The dirty_tx tracks the current buffer that is being sent by the
+ * controller.  The cur_tx and dirty_tx are equal under both completely
+ * empty and completely full conditions.  The empty/ready indicator in
+ * the buffer descriptor determines the actual condition.
+ */
+struct gfar_private
+{
+       /* pointers to arrays of skbuffs for tx and rx */
+       struct sk_buff ** tx_skbuff;
+       struct sk_buff ** rx_skbuff;
+
+       /* indices pointing to the next free sbk in skb arrays */
+       u16 skb_curtx;
+       u16 skb_currx;
+
+       /* index of the first skb which hasn't been transmitted
+        * yet. */
+       u16 skb_dirtytx;
+
+       /* Configuration info for the coalescing features */
+       unsigned char txcoalescing;
+       unsigned short txcount;
+       unsigned short txtime;
+       unsigned char rxcoalescing;
+       unsigned short rxcount;
+       unsigned short rxtime;
+
+       /* GFAR addresses */
+       struct rxbd8 *rx_bd_base;       /* Base addresses of Rx and Tx Buffers */
+       struct txbd8 *tx_bd_base;
+       struct rxbd8 *cur_rx;           /* Next free rx ring entry */
+       struct txbd8 *cur_tx;           /* Next free ring entry */
+       struct txbd8 *dirty_tx;         /* The Ring entry to be freed. */
+       struct gfar *regs;      /* Pointer to the GFAR memory mapped Registers */
+       struct phy_info *phyinfo;
+       struct gfar *phyregs;
+       struct work_struct tq;
+       struct timer_list phy_info_timer;
+       struct net_device_stats stats; /* linux network statistics */
+       struct gfar_extra_stats extra_stats;
+       spinlock_t lock;
+       unsigned int rx_buffer_size;
+       unsigned int rx_stash_size;
+       unsigned int tx_ring_size;
+       unsigned int rx_ring_size;
+       wait_queue_head_t rxcleanupq;
+       unsigned int rxclean;
+       int link;       /* current link state */
+       int oldlink;
+       int duplexity; /* Indicates negotiated duplex state */
+       int olddplx;
+       int speed;      /* Indicates negotiated speed */
+       int oldspeed;
+       
+       /* Info structure initialized by board setup code */
+       struct ocp_gfar_data *einfo;
+};
+
+extern inline u32 gfar_read(volatile unsigned *addr)
+{
+       u32 val;
+       val = in_be32(addr);
+       return val;
+}
+
+extern inline void gfar_write(volatile unsigned *addr, u32 val)
+{
+       out_be32(addr, val);
+}
+
+
+
+#endif /* __GIANFAR_H */
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
new file mode 100644 (file)
index 0000000..4ccb5af
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * drivers/net/gianfar_ethtool.c
+ *
+ * Gianfar Ethernet Driver
+ * Ethtool support for Gianfar Enet
+ * Based on e1000 ethtool support
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This software may be used and distributed according to 
+ * the terms of the GNU Public License, Version 2, incorporated herein 
+ * by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/crc32.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <linux/ethtool.h>
+
+#include "gianfar.h"
+
+#define is_power_of_2(x)        ((x) != 0 && (((x) & ((x) - 1)) == 0))
+
+extern int startup_gfar(struct net_device *dev);
+extern void stop_gfar(struct net_device *dev);
+extern void gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
+
+void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
+                    u64 * buf);
+void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
+int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo);
+
+static char stat_gstrings[][ETH_GSTRING_LEN] = {
+       "RX Dropped by Kernel",
+       "RX Large Frame Errors",
+       "RX Short Frame Errors",
+       "RX Non-Octet Errors",
+       "RX CRC Errors",
+       "RX Overrun Errors",
+       "RX Busy Errors",
+       "RX Babbling Errors",
+       "RX Truncated Frames",
+       "Ethernet Bus Error",
+       "TX Babbling Errors",
+       "TX Underrun Errors",
+       "RX SKB Missing Errors",
+       "TX Timeout Errors",
+       "tx&rx 64B frames",
+       "tx&rx 65-127B frames",
+       "tx&rx 128-255B frames",
+       "tx&rx 256-511B frames",
+       "tx&rx 512-1023B frames",
+       "tx&rx 1024-1518B frames",
+       "tx&rx 1519-1522B Good VLAN",
+       "RX bytes",
+       "RX Packets",
+       "RX FCS Errors",
+       "Receive Multicast Packet",
+       "Receive Broadcast Packet",
+       "RX Control Frame Packets",
+       "RX Pause Frame Packets",
+       "RX Unknown OP Code",
+       "RX Alignment Error",
+       "RX Frame Length Error",
+       "RX Code Error",
+       "RX Carrier Sense Error",
+       "RX Undersize Packets",
+       "RX Oversize Packets",
+       "RX Fragmented Frames",
+       "RX Jabber Frames",
+       "RX Dropped Frames",
+       "TX Byte Counter",
+       "TX Packets",
+       "TX Multicast Packets",
+       "TX Broadcast Packets",
+       "TX Pause Control Frames",
+       "TX Deferral Packets",
+       "TX Excessive Deferral Packets",
+       "TX Single Collision Packets",
+       "TX Multiple Collision Packets",
+       "TX Late Collision Packets",
+       "TX Excessive Collision Packets",
+       "TX Total Collision",
+       "RESERVED",
+       "TX Dropped Frames",
+       "TX Jabber Frames",
+       "TX FCS Errors",
+       "TX Control Frames",
+       "TX Oversize Frames",
+       "TX Undersize Frames",
+       "TX Fragmented Frames",
+};
+
+/* Fill in an array of 64-bit statistics from various sources.
+ * This array will be appended to the end of the ethtool_stats
+ * structure, and returned to user space
+ */
+void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
+{
+       int i;
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       u32 *rmon = (u32 *) & priv->regs->rmon;
+       u64 *extra = (u64 *) & priv->extra_stats;
+       struct gfar_stats *stats = (struct gfar_stats *) buf;
+
+       for (i = 0; i < GFAR_RMON_LEN; i++) {
+               stats->rmon[i] = (u64) (rmon[i]);
+       }
+
+       for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
+               stats->extra[i] = extra[i];
+       }
+}
+
+/* Returns the number of stats (and their corresponding strings) */
+int gfar_stats_count(struct net_device *dev)
+{
+       return GFAR_STATS_LEN;
+}
+
+void gfar_gstrings_normon(struct net_device *dev, u32 stringset, u8 * buf)
+{
+       memcpy(buf, stat_gstrings, GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN);
+}
+
+void gfar_fill_stats_normon(struct net_device *dev, 
+               struct ethtool_stats *dummy, u64 * buf)
+{
+       int i;
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       u64 *extra = (u64 *) & priv->extra_stats;
+
+       for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
+               buf[i] = extra[i];
+       }
+}
+
+
+int gfar_stats_count_normon(struct net_device *dev)
+{
+       return GFAR_EXTRA_STATS_LEN;
+}
+/* Fills in the drvinfo structure with some basic info */
+void gfar_gdrvinfo(struct net_device *dev, struct
+             ethtool_drvinfo *drvinfo)
+{
+       strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN);
+       strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
+       strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
+       strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
+       drvinfo->n_stats = GFAR_STATS_LEN;
+       drvinfo->testinfo_len = 0;
+       drvinfo->regdump_len = 0;
+       drvinfo->eedump_len = 0;
+}
+
+/* Return the current settings in the ethtool_cmd structure */
+int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       uint gigabit_support = 
+               priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0;
+       uint gigabit_advert = 
+               priv->einfo->flags & GFAR_HAS_GIGABIT ? ADVERTISED_1000baseT_Full: 0;
+
+       cmd->supported = (SUPPORTED_10baseT_Half
+                         | SUPPORTED_100baseT_Half
+                         | SUPPORTED_100baseT_Full
+                         | gigabit_support | SUPPORTED_Autoneg);
+
+       /* For now, we always advertise everything */
+       cmd->advertising = (ADVERTISED_10baseT_Half
+                           | ADVERTISED_100baseT_Half
+                           | ADVERTISED_100baseT_Full
+                           | gigabit_advert | ADVERTISED_Autoneg);
+
+       cmd->speed = priv->speed;
+       cmd->duplex = priv->duplexity;
+       cmd->port = PORT_MII;
+       cmd->phy_address = priv->einfo->phyid;
+       cmd->transceiver = XCVR_EXTERNAL;
+       cmd->autoneg = AUTONEG_ENABLE;
+       cmd->maxtxpkt = priv->txcount;
+       cmd->maxrxpkt = priv->rxcount;
+
+       return 0;
+}
+
+/* Return the length of the register structure */
+int gfar_reglen(struct net_device *dev)
+{
+       return sizeof (struct gfar);
+}
+
+/* Return a dump of the GFAR register space */
+void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+{
+       int i;
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       u32 *theregs = (u32 *) priv->regs;
+       u32 *buf = (u32 *) regbuf;
+
+       for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
+               buf[i] = theregs[i];
+}
+
+/* Return the link state 1 is up, 0 is down */
+u32 gfar_get_link(struct net_device *dev)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       return (u32) priv->link;
+}
+
+/* Fill in a buffer with the strings which correspond to the
+ * stats */
+void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
+{
+       memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
+}
+
+/* Convert microseconds to ethernet clock ticks, which changes
+ * depending on what speed the controller is running at */
+static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs)
+{
+       unsigned int count;
+
+       /* The timer is different, depending on the interface speed */
+       switch (priv->speed) {
+       case 1000:
+               count = GFAR_GBIT_TIME;
+               break;
+       case 100:
+               count = GFAR_100_TIME;
+               break;
+       case 10:
+       default:
+               count = GFAR_10_TIME;
+               break;
+       }
+
+       /* Make sure we return a number greater than 0
+        * if usecs > 0 */
+       return ((usecs * 1000 + count - 1) / count);
+}
+
+/* Convert ethernet clock ticks to microseconds */
+static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int ticks)
+{
+       unsigned int count;
+
+       /* The timer is different, depending on the interface speed */
+       switch (priv->speed) {
+       case 1000:
+               count = GFAR_GBIT_TIME;
+               break;
+       case 100:
+               count = GFAR_100_TIME;
+               break;
+       case 10:
+       default:
+               count = GFAR_10_TIME;
+               break;
+       }
+
+       /* Make sure we return a number greater than 0 */
+       /* if ticks is > 0 */
+       return ((ticks * count) / 1000);
+}
+
+/* Get the coalescing parameters, and put them in the cvals
+ * structure.  */
+int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+       cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
+       cvals->rx_max_coalesced_frames = priv->rxcount;
+
+       cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, priv->txtime);
+       cvals->tx_max_coalesced_frames = priv->txcount;
+
+       cvals->use_adaptive_rx_coalesce = 0;
+       cvals->use_adaptive_tx_coalesce = 0;
+
+       cvals->pkt_rate_low = 0;
+       cvals->rx_coalesce_usecs_low = 0;
+       cvals->rx_max_coalesced_frames_low = 0;
+       cvals->tx_coalesce_usecs_low = 0;
+       cvals->tx_max_coalesced_frames_low = 0;
+
+       /* When the packet rate is below pkt_rate_high but above
+        * pkt_rate_low (both measured in packets per second) the
+        * normal {rx,tx}_* coalescing parameters are used.
+        */
+
+       /* When the packet rate is (measured in packets per second)
+        * is above pkt_rate_high, the {rx,tx}_*_high parameters are
+        * used.
+        */
+       cvals->pkt_rate_high = 0;
+       cvals->rx_coalesce_usecs_high = 0;
+       cvals->rx_max_coalesced_frames_high = 0;
+       cvals->tx_coalesce_usecs_high = 0;
+       cvals->tx_max_coalesced_frames_high = 0;
+
+       /* How often to do adaptive coalescing packet rate sampling,
+        * measured in seconds.  Must not be zero.
+        */
+       cvals->rate_sample_interval = 0;
+
+       return 0;
+}
+
+/* Change the coalescing values.
+ * Both cvals->*_usecs and cvals->*_frames have to be > 0
+ * in order for coalescing to be active
+ */
+int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+       /* Set up rx coalescing */
+       if ((cvals->rx_coalesce_usecs == 0) ||
+           (cvals->rx_max_coalesced_frames == 0))
+               priv->rxcoalescing = 0;
+       else
+               priv->rxcoalescing = 1;
+
+       priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs);
+       priv->rxcount = cvals->rx_max_coalesced_frames;
+
+       /* Set up tx coalescing */
+       if ((cvals->tx_coalesce_usecs == 0) ||
+           (cvals->tx_max_coalesced_frames == 0))
+               priv->txcoalescing = 0;
+       else
+               priv->txcoalescing = 1;
+
+       priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs);
+       priv->txcount = cvals->tx_max_coalesced_frames;
+
+       if (priv->rxcoalescing)
+               gfar_write(&priv->regs->rxic,
+                          mk_ic_value(priv->rxcount, priv->rxtime));
+       else
+               gfar_write(&priv->regs->rxic, 0);
+
+       if (priv->txcoalescing)
+               gfar_write(&priv->regs->txic,
+                          mk_ic_value(priv->txcount, priv->txtime));
+       else
+               gfar_write(&priv->regs->txic, 0);
+
+       return 0;
+}
+
+/* Fills in rvals with the current ring parameters.  Currently,
+ * rx, rx_mini, and rx_jumbo rings are the same size, as mini and
+ * jumbo are ignored by the driver */
+void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+       rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
+       rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
+       rvals->rx_jumbo_max_pending = GFAR_RX_MAX_RING_SIZE;
+       rvals->tx_max_pending = GFAR_TX_MAX_RING_SIZE;
+
+       /* Values changeable by the user.  The valid values are
+        * in the range 1 to the "*_max_pending" counterpart above.
+        */
+       rvals->rx_pending = priv->rx_ring_size;
+       rvals->rx_mini_pending = priv->rx_ring_size;
+       rvals->rx_jumbo_pending = priv->rx_ring_size;
+       rvals->tx_pending = priv->tx_ring_size;
+}
+
+/* Change the current ring parameters, stopping the controller if
+ * necessary so that we don't mess things up while we're in
+ * motion.  We wait for the ring to be clean before reallocating
+ * the rings. */
+int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+{
+       u32 tempval;
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       int err = 0;
+
+       if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
+               return -EINVAL;
+
+       if (!is_power_of_2(rvals->rx_pending)) {
+               printk("%s: Ring sizes must be a power of 2\n",
+                               dev->name);
+               return -EINVAL;
+       }
+
+       if (rvals->tx_pending > GFAR_TX_MAX_RING_SIZE)
+               return -EINVAL;
+
+       if (!is_power_of_2(rvals->tx_pending)) {
+               printk("%s: Ring sizes must be a power of 2\n",
+                               dev->name);
+               return -EINVAL;
+       }
+
+       /* Stop the controller so we don't rx any more frames */
+       /* But first, make sure we clear the bits */
+       tempval = gfar_read(&priv->regs->dmactrl);
+       tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
+       gfar_write(&priv->regs->dmactrl, tempval);
+
+       tempval = gfar_read(&priv->regs->dmactrl);
+       tempval |= (DMACTRL_GRS | DMACTRL_GTS);
+       gfar_write(&priv->regs->dmactrl, tempval);
+
+       while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC)))
+               cpu_relax();
+
+       /* Note that rx is not clean right now */
+       priv->rxclean = 0;
+
+       if (dev->flags & IFF_UP) {
+               /* Tell the driver to process the rest of the frames */
+               gfar_receive(0, (void *) dev, NULL);
+
+               /* Now wait for it to be done */
+               wait_event_interruptible(priv->rxcleanupq, priv->rxclean);
+
+               /* Ok, all packets have been handled.  Now we bring it down,
+                * change the ring size, and bring it up */
+
+               stop_gfar(dev);
+       }
+
+       priv->rx_ring_size = rvals->rx_pending;
+       priv->tx_ring_size = rvals->tx_pending;
+
+       if (dev->flags & IFF_UP)
+               err = startup_gfar(dev);
+
+       return err;
+}
+
+struct ethtool_ops gfar_ethtool_ops = {
+       .get_settings = gfar_gsettings,
+       .get_drvinfo = gfar_gdrvinfo,
+       .get_regs_len = gfar_reglen,
+       .get_regs = gfar_get_regs,
+       .get_link = gfar_get_link,
+       .get_coalesce = gfar_gcoalesce,
+       .set_coalesce = gfar_scoalesce,
+       .get_ringparam = gfar_gringparam,
+       .set_ringparam = gfar_sringparam,
+       .get_strings = gfar_gstrings,
+       .get_stats_count = gfar_stats_count,
+       .get_ethtool_stats = gfar_fill_stats,
+};
diff --git a/drivers/net/gianfar_phy.c b/drivers/net/gianfar_phy.c
new file mode 100644 (file)
index 0000000..ea02e5d
--- /dev/null
@@ -0,0 +1,622 @@
+/* 
+ * drivers/net/gianfar_phy.c
+ *
+ * Gianfar Ethernet Driver -- PHY handling
+ * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/mii.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/crc32.h>
+
+#include "gianfar.h"
+#include "gianfar_phy.h"
+
+/* Write value to the PHY for this device to the register at regnum, */
+/* waiting until the write is done before it returns.  All PHY */
+/* configuration has to be done through the TSEC1 MIIM regs */
+void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       struct gfar *regbase = priv->phyregs;
+       struct ocp_gfar_data *einfo = priv->einfo;
+
+       /* Set the PHY address and the register address we want to write */
+       gfar_write(&regbase->miimadd, ((einfo->phyid) << 8) | regnum);
+
+       /* Write out the value we want */
+       gfar_write(&regbase->miimcon, value);
+
+       /* Wait for the transaction to finish */
+       while (gfar_read(&regbase->miimind) & MIIMIND_BUSY)
+               cpu_relax();
+}
+
+/* Reads from register regnum in the PHY for device dev, */
+/* returning the value.  Clears miimcom first.  All PHY */
+/* configuration has to be done through the TSEC1 MIIM regs */
+u16 read_phy_reg(struct net_device *dev, u16 regnum)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       struct gfar *regbase = priv->phyregs;
+       struct ocp_gfar_data *einfo = priv->einfo;
+       u16 value;
+
+       /* Set the PHY address and the register address we want to read */
+       gfar_write(&regbase->miimadd, ((einfo->phyid) << 8) | regnum);
+
+       /* Clear miimcom, and then initiate a read */
+       gfar_write(&regbase->miimcom, 0);
+       gfar_write(&regbase->miimcom, MIIM_READ_COMMAND);
+
+       /* Wait for the transaction to finish */
+       while (gfar_read(&regbase->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
+               cpu_relax();
+
+       /* Grab the value of the register from miimstat */
+       value = gfar_read(&regbase->miimstat);
+
+       return value;
+}
+
+/* returns which value to write to the control register. */
+/* For 10/100 the value is slightly different. */
+u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       struct ocp_gfar_data *einfo = priv->einfo;
+
+       if (einfo->flags & GFAR_HAS_GIGABIT)
+               return MIIM_CONTROL_INIT;
+       else
+               return MIIM_CR_INIT;
+}
+
+#define BRIEF_GFAR_ERRORS
+/* Wait for auto-negotiation to complete */
+u16 mii_parse_sr(u16 mii_reg, struct net_device * dev)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+       unsigned int timeout = GFAR_AN_TIMEOUT;
+
+       if (mii_reg & MIIM_STATUS_LINK)
+               priv->link = 1;
+       else
+               priv->link = 0;
+
+       /* Only auto-negotiate if the link has just gone up */
+       if (priv->link && !priv->oldlink) {
+               while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
+                       mii_reg = read_phy_reg(dev, MIIM_STATUS);
+
+#if defined(BRIEF_GFAR_ERRORS)
+               if (mii_reg & MIIM_STATUS_AN_DONE)
+                       printk(KERN_INFO "%s: Auto-negotiation done\n",
+                              dev->name);
+               else
+                       printk(KERN_INFO "%s: Auto-negotiation timed out\n",
+                              dev->name);
+#endif
+       }
+
+       return 0;
+}
+
+/* Determine the speed and duplex which was negotiated */
+u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       unsigned int speed;
+
+       if (priv->link) {
+               if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
+                       priv->duplexity = 1;
+               else
+                       priv->duplexity = 0;
+
+               speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
+
+               switch (speed) {
+               case MIIM_88E1011_PHYSTAT_GBIT:
+                       priv->speed = 1000;
+                       break;
+               case MIIM_88E1011_PHYSTAT_100:
+                       priv->speed = 100;
+                       break;
+               default:
+                       priv->speed = 10;
+                       break;
+               }
+       } else {
+               priv->speed = 0;
+               priv->duplexity = 0;
+       }
+
+       return 0;
+}
+
+u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       unsigned int speed;
+
+       if (priv->link) {
+               if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
+                       priv->duplexity = 1;
+               else
+                       priv->duplexity = 0;
+
+               speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
+
+               switch (speed) {
+               case MIIM_CIS8201_AUXCONSTAT_GBIT:
+                       priv->speed = 1000;
+                       break;
+               case MIIM_CIS8201_AUXCONSTAT_100:
+                       priv->speed = 100;
+                       break;
+               default:
+                       priv->speed = 10;
+                       break;
+               }
+       } else {
+               priv->speed = 0;
+               priv->duplexity = 0;
+       }
+
+       return 0;
+}
+
+u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+       if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
+               priv->speed = 100;
+       else
+               priv->speed = 10;
+
+       if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
+               priv->duplexity = 1;
+       else
+               priv->duplexity = 0;
+
+       return 0;
+}
+
+u16 dm9161_wait(u16 mii_reg, struct net_device *dev)
+{
+       int timeout = HZ;
+       int secondary = 10;
+       u16 temp;
+
+       do {
+
+               /* Davicom takes a bit to come up after a reset,
+                * so wait here for a bit */
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(timeout);
+
+               temp = read_phy_reg(dev, MIIM_STATUS);
+
+               secondary--;
+       } while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary);
+
+       return 0;
+}
+
+/*
+ * consult the BCM54xx auxilliary status register to find the link settings
+ */
+u16 mii_parse_bcm54xx_sr(u16 mii_reg, struct net_device * dev)
+{
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+       /* Link modes of the BCM5400 PHY */
+       static const uint16_t link_table[8][3] = {
+               { 0, 0          },      /* No link */
+               { 0, 10         },      /* 10BT Half Duplex */
+               { 1, 10         },      /* 10BT Full Duplex */
+               { 0, 100        },      /* 100BT Half Duplex */
+               { 0, 100        },      /* 100BT Half Duplex */
+               { 1, 100        },      /* 100BT Full Duplex*/
+               { 1, 1000       },      /* 1000BT */
+               { 1, 1000       },      /* 1000BT */
+       };
+
+       uint16_t link_mode;
+
+       link_mode = mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK;
+       link_mode >>= MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT;
+
+       priv->duplexity = link_table[link_mode][0];
+       priv->speed = link_table[link_mode][1];
+
+       return 0;
+}
+
+static struct phy_info phy_info_M88E1011S = {
+       0x01410c6,
+       "Marvell 88E1011S",
+       4,
+       (const struct phy_cmd[]) {      /* config */
+               /* Reset and configure the PHY */
+               {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* startup */
+               /* Status is read once to clear old link state */
+               {MIIM_STATUS, miim_read, NULL},
+               /* Auto-negotiate */
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+               /* Read the status */
+               {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
+               /* Clear the IEVENT register */
+               {MIIM_88E1011_IEVENT, miim_read, NULL},
+               /* Set up the mask */
+               {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* ack_int */
+               /* Clear the interrupt */
+               {MIIM_88E1011_IEVENT, miim_read, NULL},
+               /* Disable interrupts */
+               {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* handle_int */
+               /* Read the Status (2x to make sure link is right) */
+               {MIIM_STATUS, miim_read, NULL},
+               /* Check the status */
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+               {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
+                       /* Enable Interrupts */
+               {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* shutdown */
+               {MIIM_88E1011_IEVENT, miim_read, NULL},
+               {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
+               {miim_end,}
+       },
+};
+
+/* Cicada 8204 */
+static struct phy_info phy_info_cis8204 = {
+       0x3f11,
+       "Cicada Cis8204",
+       6,
+       (const struct phy_cmd[]) {      /* config */
+               /* Override PHY config settings */
+               {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
+               /* Set up the interface mode */
+               {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
+               /* Configure some basic stuff */
+               {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* startup */
+               /* Read the Status (2x to make sure link is right) */
+               {MIIM_STATUS, miim_read, NULL},
+               /* Auto-negotiate */
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+               /* Read the status */
+               {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
+               /* Clear the status register */
+               {MIIM_CIS8204_ISTAT, miim_read, NULL},
+               /* Enable interrupts */
+               {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* ack_int */
+               /* Clear the status register */
+               {MIIM_CIS8204_ISTAT, miim_read, NULL},
+               /* Disable interrupts */
+               {MIIM_CIS8204_IMASK, 0x0, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* handle_int */
+               /* Read the Status (2x to make sure link is right) */
+               {MIIM_STATUS, miim_read, NULL},
+               /* Auto-negotiate */
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+               /* Read the status */
+               {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
+               /* Enable interrupts */
+               {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* shutdown */
+               /* Clear the status register */
+               {MIIM_CIS8204_ISTAT, miim_read, NULL},
+               /* Disable interrupts */
+               {MIIM_CIS8204_IMASK, 0x0, NULL},
+               {miim_end,}
+       },
+};
+
+/* Cicada 8201 */
+static struct phy_info phy_info_cis8201 = {
+       0xfc41,
+       "CIS8201",
+       4,
+       (const struct phy_cmd[]) {      /* config */
+               /* Override PHY config settings */
+               {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
+               /* Set up the interface mode */
+               {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
+               /* Configure some basic stuff */
+               {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* startup */
+               /* Read the Status (2x to make sure link is right) */
+               {MIIM_STATUS, miim_read, NULL},
+               /* Auto-negotiate */
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+               /* Read the status */
+               {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* ack_int */
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* handle_int */
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* shutdown */
+               {miim_end,}
+       },
+};
+
+static struct phy_info phy_info_dm9161 = {
+       0x0181b88,
+       "Davicom DM9161E",
+       4,
+       (const struct phy_cmd[]) {      /* config */
+               {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
+               /* Do not bypass the scrambler/descrambler */
+               {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
+               /* Clear 10BTCSR to default */
+               {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
+               /* Configure some basic stuff */
+               {MIIM_CONTROL, MIIM_CR_INIT, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* startup */
+               /* Restart Auto Negotiation */
+               {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
+               /* Status is read once to clear old link state */
+               {MIIM_STATUS, miim_read, dm9161_wait},
+               /* Auto-negotiate */
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+               /* Read the status */
+               {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
+               /* Clear any pending interrupts */
+               {MIIM_DM9161_INTR, miim_read, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* ack_int */
+               {MIIM_DM9161_INTR, miim_read, NULL},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* handle_int */
+               {MIIM_STATUS, miim_read, NULL},
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+               {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
+               {miim_end,}
+       },
+       (const struct phy_cmd[]) {      /* shutdown */
+               {MIIM_DM9161_INTR, miim_read, NULL},
+               {miim_end,}
+       },
+};
+
+/* Broadcom BCM5421S PHY */
+static struct phy_info phy_info_bcm5421s = {
+       .id = 0x2060E1,
+       .name = "Broadcom BCM5421S",
+       .shift = 0,
+       .config = (const struct phy_cmd[]) {
+               /* Configure some basic stuff */
+               {MIIM_CONTROL, MIIM_CR_INIT, NULL},
+#if 0 /* 5421 only */
+               miim_write(MII_BCM5400_AUXCONTROL, 0x1007),
+               miim_set_bits(MII_BCM5400_AUXCONTROL, 0x0400),
+               miim_write(MII_BCM5400_AUXCONTROL, 0x0007),
+               miim_set_bits(MII_BCM5400_AUXCONTROL, 0x0800),
+               miim_write(0x17, 0x000a),
+               miim_set_bits(MII_RERRCOUNTER, 0x0200),
+#endif
+#if 0 /* enable automatic low power */
+               miim_write(MII_NCONFIG, 0x9002),
+               miim_write(MII_NCONFIG, 0xa821),
+               miim_write(MII_NCONFIG, 0x941d),
+#endif
+               {miim_end,}
+       },
+       .startup = (const struct phy_cmd[]) {
+               /* Restart Auto Negotiation */
+               miim_set_bits(MIIM_CONTROL, BMCR_ANENABLE | BMCR_ANRESTART),
+#if 0
+               /* Status is read once to clear old link state */
+               {MIIM_STATUS, miim_read, dm9161_wait},
+#endif
+               /* Auto-negotiate */
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+
+               /* Read the link status */
+               {MIIM_BCM54xx_AUXSTATUS, miim_read, mii_parse_bcm54xx_sr},
+
+               {miim_end,}
+       },
+       .ack_int = (const struct phy_cmd[]) {
+               {miim_end,}
+       },
+       .handle_int = (const struct phy_cmd[]) {
+               {MIIM_STATUS, miim_read, NULL},
+               {MIIM_STATUS, miim_read, mii_parse_sr},
+               {miim_end,}
+       },
+       .shutdown = (const struct phy_cmd[]) {
+               {miim_end,}
+       },
+};
+
+static struct phy_info *phy_info[] = {
+       &phy_info_cis8201,
+       &phy_info_cis8204,
+       &phy_info_M88E1011S,
+       &phy_info_dm9161,
+       &phy_info_bcm5421s,
+       NULL
+};
+
+/* Use the PHY ID registers to determine what type of PHY is attached
+ * to device dev.  return a struct phy_info structure describing that PHY
+ */
+struct phy_info * get_phy_info(struct net_device *dev)
+{
+       u16 phy_reg;
+       u32 phy_ID;
+       int i;
+       struct phy_info *theInfo = NULL;
+
+       /* Grab the bits from PHYIR1, and put them in the upper half */
+       phy_reg = read_phy_reg(dev, MIIM_PHYIR1);
+       phy_ID = (phy_reg & 0xffff) << 16;
+
+       /* Grab the bits from PHYIR2, and put them in the lower half */
+       phy_reg = read_phy_reg(dev, MIIM_PHYIR2);
+       phy_ID |= (phy_reg & 0xffff);
+
+       /* loop through all the known PHY types, and find one that */
+       /* matches the ID we read from the PHY. */
+       for (i = 0; phy_info[i]; i++)
+               if (phy_info[i]->id == (phy_ID >> phy_info[i]->shift))
+                       theInfo = phy_info[i];
+
+       if (theInfo == NULL) {
+               printk("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
+               return NULL;
+       } else {
+               printk("%s: PHY is %s (%x)\n", dev->name, theInfo->name,
+                      phy_ID);
+       }
+
+       return theInfo;
+}
+
+/* Take a list of struct phy_cmd, and, depending on the values, either */
+/* read or write, using a helper function if provided */
+/* It is assumed that all lists of struct phy_cmd will be terminated by */
+/* mii_end. */
+void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
+{
+       int i;
+       u16 result;
+       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       struct gfar *phyregs = priv->phyregs;
+
+       /* Reset the management interface */
+       gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
+
+       /* Setup the MII Mgmt clock speed */
+       gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
+
+       /* Wait until the bus is free */
+       while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
+               cpu_relax();
+
+       for (i = 0; cmd->mii_reg != miim_end; i++) {
+               switch (cmd->mii_data >> 16) {
+               case 0x0000:
+                       /* Otherwise, it's a write */
+                       /* If a function was supplied, it will provide 
+                        * the value to write */
+                       /* Otherwise, the value was supplied in cmd->mii_data */
+                       if (cmd->funct != NULL)
+                               result = (*(cmd->funct)) (0, dev);
+                       else
+                               result = cmd->mii_data;
+
+                       write_phy_reg(dev, cmd->mii_reg, result);
+                       break;
+
+               case 0x0001:
+                       /* Read the value of the PHY reg */
+                       result = read_phy_reg(dev, cmd->mii_reg);
+
+                       /* If a function was supplied, we need to let it process */
+                       /* the result. */
+                       if (cmd->funct != NULL)
+                               (*(cmd->funct)) (result, dev);
+                       break;
+
+               case 0x0002:
+                       /* read the value, clear some bits and write it back */
+                       BUG_ON(cmd->funct);
+
+                       result = read_phy_reg(dev, cmd->mii_reg);
+                       result &= cmd->mii_data;
+                       write_phy_reg(dev, cmd->mii_reg, result);
+                       break;
+
+               case 0x0003:
+                       /* read the value, set some bits and write it back */
+                       BUG_ON(cmd->funct);
+
+                       result = read_phy_reg(dev, cmd->mii_reg);
+                       result &= cmd->mii_data;
+                       write_phy_reg(dev, cmd->mii_reg, result);
+                       break;
+
+               case 0x0004:
+                       /* read the value, flip some bits and write it back */
+                       BUG_ON(cmd->funct);
+
+                       result = read_phy_reg(dev, cmd->mii_reg);
+                       result &= cmd->mii_data;
+                       write_phy_reg(dev, cmd->mii_reg, result);
+                       break;
+
+               default:
+                       printk("GIANFAR: Unknown MII command %08x\n",
+                              cmd->mii_data);
+                       BUG();
+               }
+               cmd++;
+       }
+}
diff --git a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h
new file mode 100644 (file)
index 0000000..df4c0ec
--- /dev/null
@@ -0,0 +1,202 @@
+/* 
+ * drivers/net/gianfar_phy.h
+ *
+ * Gianfar Ethernet Driver -- PHY handling
+ * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+#ifndef __GIANFAR_PHY_H
+#define __GIANFAR_PHY_H
+
+/* simple datum processing commands */
+#define miim_end               (0xffff0000U)
+#define miim_read              (0x00010000U)
+#define miim_clear_bits(reg,x) { reg, (0x00020000U | ~(u32)(x)), NULL }
+#define miim_set_bits(reg,x)   { reg, (0x00030000U | (u32)(x)),  NULL }
+#define miim_flip_bits(reg,x)  { reg, (0x00040000U | (u32)(x)),  NULL }
+#define miim_write(reg, x)     { reg, (0x0000ffffU & (u32)(x)),  NULL }
+
+#define MIIMIND_BUSY            0x00000001
+#define MIIMIND_NOTVALID        0x00000004
+
+#define MIIM_CONTROL           0x00
+#define MIIM_CONTROL_RESET     0x00008000
+#define MIIM_CONTROL_INIT      0x00001140
+#define MIIM_ANEN              0x00001000
+
+#define MIIM_CR                 0x00
+#define MIIM_CR_RST            0x00008000
+#define MIIM_CR_INIT           0x00001000
+
+#define MIIM_STATUS            0x1
+#define MIIM_STATUS_AN_DONE    0x00000020
+#define MIIM_STATUS_LINK       0x0004
+
+#define MIIM_PHYIR1            0x2
+#define MIIM_PHYIR2            0x3
+
+#define GFAR_AN_TIMEOUT         0x000fffff
+
+#define MIIM_ANLPBPA   0x5
+#define MIIM_ANLPBPA_HALF      0x00000040
+#define MIIM_ANLPBPA_FULL      0x00000020
+
+#define MIIM_ANEX              0x6
+#define MIIM_ANEX_NP           0x00000004
+#define MIIM_ANEX_PRX          0x00000002
+
+
+/* Cicada Extended Control Register 1 */
+#define MIIM_CIS8201_EXT_CON1           0x17
+#define MIIM_CIS8201_EXTCON1_INIT       0x0000
+
+/* Cicada Interrupt Mask Register */
+#define MIIM_CIS8204_IMASK             0x19
+#define MIIM_CIS8204_IMASK_IEN         0x8000
+#define MIIM_CIS8204_IMASK_SPEED       0x4000
+#define MIIM_CIS8204_IMASK_LINK                0x2000
+#define MIIM_CIS8204_IMASK_DUPLEX      0x1000
+#define MIIM_CIS8204_IMASK_MASK                0xf000
+
+/* Cicada Interrupt Status Register */
+#define MIIM_CIS8204_ISTAT             0x1a
+#define MIIM_CIS8204_ISTAT_STATUS      0x8000
+#define MIIM_CIS8204_ISTAT_SPEED       0x4000
+#define MIIM_CIS8204_ISTAT_LINK                0x2000
+#define MIIM_CIS8204_ISTAT_DUPLEX      0x1000
+
+/* Cicada Auxiliary Control/Status Register */
+#define MIIM_CIS8201_AUX_CONSTAT        0x1c
+#define MIIM_CIS8201_AUXCONSTAT_INIT    0x0004
+#define MIIM_CIS8201_AUXCONSTAT_DUPLEX  0x0020
+#define MIIM_CIS8201_AUXCONSTAT_SPEED   0x0018
+#define MIIM_CIS8201_AUXCONSTAT_GBIT    0x0010
+#define MIIM_CIS8201_AUXCONSTAT_100     0x0008
+                                                                                
+/* 88E1011 PHY Status Register */
+#define MIIM_88E1011_PHY_STATUS         0x11
+#define MIIM_88E1011_PHYSTAT_SPEED      0xc000
+#define MIIM_88E1011_PHYSTAT_GBIT       0x8000
+#define MIIM_88E1011_PHYSTAT_100        0x4000
+#define MIIM_88E1011_PHYSTAT_DUPLEX     0x2000
+#define MIIM_88E1011_PHYSTAT_LINK      0x0400
+
+#define MIIM_88E1011_IEVENT            0x13
+#define MIIM_88E1011_IEVENT_CLEAR      0x0000
+
+#define MIIM_88E1011_IMASK             0x12
+#define MIIM_88E1011_IMASK_INIT                0x6400
+#define MIIM_88E1011_IMASK_CLEAR       0x0000
+
+/* DM9161 Control register values */
+#define MIIM_DM9161_CR_STOP    0x0400
+#define MIIM_DM9161_CR_RSTAN   0x1200
+
+#define MIIM_DM9161_SCR                0x10
+#define MIIM_DM9161_SCR_INIT   0x0610
+
+/* DM9161 Specified Configuration and Status Register */
+#define MIIM_DM9161_SCSR       0x11
+#define MIIM_DM9161_SCSR_100F  0x8000
+#define MIIM_DM9161_SCSR_100H  0x4000
+#define MIIM_DM9161_SCSR_10F   0x2000
+#define MIIM_DM9161_SCSR_10H   0x1000
+
+/* DM9161 Interrupt Register */
+#define MIIM_DM9161_INTR       0x15
+#define MIIM_DM9161_INTR_PEND          0x8000
+#define MIIM_DM9161_INTR_DPLX_MASK     0x0800
+#define MIIM_DM9161_INTR_SPD_MASK      0x0400
+#define MIIM_DM9161_INTR_LINK_MASK     0x0200
+#define MIIM_DM9161_INTR_MASK          0x0100
+#define MIIM_DM9161_INTR_DPLX_CHANGE   0x0010
+#define MIIM_DM9161_INTR_SPD_CHANGE    0x0008
+#define MIIM_DM9161_INTR_LINK_CHANGE   0x0004
+#define MIIM_DM9161_INTR_INIT          0x0000
+#define MIIM_DM9161_INTR_STOP  \
+(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \
+ | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK)
+
+/* DM9161 10BT Configuration/Status */
+#define MIIM_DM9161_10BTCSR    0x12
+#define MIIM_DM9161_10BTCSR_INIT       0x7800
+
+/* BCM54xx regs */
+#define MIIM_BCM54xx_AUXCONTROL        0x18
+#define MIIM_BCM54xx_AUXSTATUS 0x19
+#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK     0x0700
+#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT    8  
+
+#define MIIM_READ_COMMAND       0x00000001
+
+/*
+ * struct phy_cmd:  A command for reading or writing a PHY register
+ *
+ * mii_reg:  The register to read or write
+ *
+ * mii_data:  For writes, the value to put in the register.
+ *     A value of -1 indicates this is a read.
+ *
+ * funct: A function pointer which is invoked for each command.
+ *     For reads, this function will be passed the value read
+ *     from the PHY, and process it.
+ *     For writes, the result of this function will be written
+ *     to the PHY register
+ */
+struct phy_cmd {
+    u32 mii_reg;
+    u32 mii_data;
+    u16 (*funct) (u16 mii_reg, struct net_device * dev);
+};
+
+/* struct phy_info: a structure which defines attributes for a PHY
+ *
+ * id will contain a number which represents the PHY.  During
+ * startup, the driver will poll the PHY to find out what its
+ * UID--as defined by registers 2 and 3--is.  The 32-bit result
+ * gotten from the PHY will be shifted right by "shift" bits to
+ * discard any bits which may change based on revision numbers
+ * unimportant to functionality
+ *
+ * The struct phy_cmd entries represent pointers to an arrays of
+ * commands which tell the driver what to do to the PHY.
+ */
+struct phy_info {
+    u32 id;
+    char *name;
+    unsigned int shift;
+    /* Called to configure the PHY, and modify the controller
+     * based on the results */
+    const struct phy_cmd *config;
+
+    /* Called when starting up the controller.  Usually sets
+     * up the interrupt for state changes */
+    const struct phy_cmd *startup;
+
+    /* Called inside the interrupt handler to acknowledge
+     * the interrupt */
+    const struct phy_cmd *ack_int;
+
+    /* Called in the bottom half to handle the interrupt */
+    const struct phy_cmd *handle_int;
+
+    /* Called when bringing down the controller.  Usually stops
+     * the interrupts from being generated */
+    const struct phy_cmd *shutdown;
+};
+
+struct phy_info *get_phy_info(struct net_device *dev);
+void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd);
+
+#endif /* GIANFAR_PHY_H */
diff --git a/drivers/serial/mpsc/Makefile b/drivers/serial/mpsc/Makefile
new file mode 100644 (file)
index 0000000..7d9054d
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Make file for the Marvell MPSC driver.
+#
+
+obj-$(CONFIG_SERIAL_MPSC)      += mpsc.o
+obj-$(CONFIG_PPC32)            += mpsc_ppc32.o
diff --git a/drivers/serial/mpsc/mpsc.c b/drivers/serial/mpsc/mpsc.c
new file mode 100644 (file)
index 0000000..de7f97c
--- /dev/null
@@ -0,0 +1,1455 @@
+/*
+ * drivers/serial/mpsc/mpsc.c
+ *
+ * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240,
+ * GT64260, MV64340, MV64360, GT96100, ... ).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * Based on an old MPSC driver that was in the linuxppc tree.  It appears to
+ * have been created by Chris Zankel (formerly of MontaVista) but there
+ * is no proper Copyright so I'm not sure.  Parts were, apparently, also
+ * taken from PPCBoot (now U-Boot).  Also based on drivers/serial/8250.c
+ * by Russell King.
+ *
+ * 2004 (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.
+ */
+/*
+ * The MPSC interface is much like a typical network controller's interface.
+ * That is, you set up separate rings of descriptors for transmitting and
+ * receiving data.  There is also a pool of buffers with (one buffer per
+ * descriptor) that incoming data are dma'd into or outgoing data are dma'd
+ * out of.
+ *
+ * The MPSC requires two other controllers to be able to work.  The Baud Rate
+ * Generator (BRG) provides a clock at programmable frequencies which determines
+ * the baud rate.  The Serial DMA Controller (SDMA) takes incoming data from the
+ * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the
+ * MPSC.  It is actually the SDMA interrupt that the driver uses to keep the
+ * transmit and receive "engines" going (i.e., indicate data has been
+ * transmitted or received).
+ *
+ * NOTES:
+ *
+ * 1) Some chips have an erratum where several regs cannot be
+ * read.  To work around that, we keep a local copy of those regs in
+ * 'mpsc_port_info_t' and use the *_M macros when accessing those regs.
+ *
+ * 2) Some chips have an erratum where the chip will hang when the SDMA ctlr
+ * accesses system mem in a cache coherent region.  This *should* be a
+ * show-stopper when coherency is turned on but it seems to work okay as
+ * long as there are no snoop hits.  Therefore, there are explicit cache
+ * management macros in addition to the dma_* calls--the dma_* calls don't
+ * do cache mgmt on coherent systems--to manage the cache ensuring there
+ * are no snoop hits.
+ *
+ * 3) AFAICT, hardware flow control isn't supported by the controller --MAG.
+ */
+
+#include "mpsc.h"
+
+/*
+ * Define how this driver is known to the outside (we've been assigned a
+ * range on the "Low-density serial ports" major).
+ */
+#define MPSC_MAJOR             204
+#define MPSC_MINOR_START       5       /* XXXX */
+#define        MPSC_DRIVER_NAME        "MPSC"
+#define        MPSC_DEVFS_NAME         "ttym/"
+#define        MPSC_DEV_NAME           "ttyM"
+#define        MPSC_VERSION            "1.00"
+
+static mpsc_port_info_t        mpsc_ports[MPSC_NUM_CTLRS];
+
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+/*
+ ******************************************************************************
+ *
+ * Baud Rate Generator Routines (BRG)
+ *
+ ******************************************************************************
+ */
+static void
+mpsc_brg_init(mpsc_port_info_t *pi, u32 clk_src)
+{
+       if (pi->brg_can_tune) {
+               MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 1, 25, 0);
+       }
+
+       MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 4, 18, clk_src);
+       MPSC_MOD_FIELD(pi, brg, BRG_BTR, 16, 0, 0);
+       return;
+}
+
+static void
+mpsc_brg_enable(mpsc_port_info_t *pi)
+{
+       MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 1, 16, 1);
+       return;
+}
+
+static void
+mpsc_brg_disable(mpsc_port_info_t *pi)
+{
+       MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 1, 16, 0);
+       return;
+}
+
+static inline void
+mpsc_set_baudrate(mpsc_port_info_t *pi, u32 baud)
+{
+       /*
+        * To set the baud, we adjust the CDV field in the BRG_BCR reg.
+        * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1.
+        * However, the input clock is divided by 16 in the MPSC b/c of how
+        * 'MPSC_MMCRH' was set up so we have to divide 'clk' used in our
+        * calculation by 16 to account for that.  So the real calculation
+        * that accounts for the way the mpsc is set up is:
+        * CDV = (clk / (baud*32)) - 1 ==> CDV = (clk / (baud << 5)) -1.
+        */
+       u32     cdv = (pi->port.uartclk/(baud << 5)) - 1;
+
+       mpsc_brg_disable(pi);
+       MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 16, 0, cdv);
+       mpsc_brg_enable(pi);
+
+       return;
+}
+
+/*
+ ******************************************************************************
+ *
+ * Serial DMA Routines (SDMA)
+ *
+ ******************************************************************************
+ */
+
+static void
+mpsc_sdma_burstsize(mpsc_port_info_t *pi, u32 burst_size)
+{
+       u32     v;
+
+       DBG("mpsc_sdma_burstsize[%d]: burst_size: %d\n",
+               pi->port.line, burst_size);
+
+       burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */
+
+       if (burst_size < 2) v = 0x0;            /* 1 64-bit word */
+       else if (burst_size < 4) v = 0x1;       /* 2 64-bit words */
+       else if (burst_size < 8) v = 0x2;       /* 4 64-bit words */
+       else v = 0x3;                           /* 8 64-bit words */
+
+       MPSC_MOD_FIELD(pi, sdma, SDMA_SDC, 2, 12, v);
+       return;
+}
+
+static void
+mpsc_sdma_init(mpsc_port_info_t *pi, u32 burst_size)
+{
+       DBG("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line, burst_size);
+
+       MPSC_MOD_FIELD(pi, sdma, SDMA_SDC, 10, 0, 0x03f);
+       mpsc_sdma_burstsize(pi, burst_size);
+       return;
+}
+
+static inline u32
+mpsc_sdma_intr_mask(mpsc_port_info_t *pi, u32 mask)
+{
+       u32     old, v;
+
+       DBG("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask);
+
+       old = v = MPSC_READ_M(pi, sdma_intr, SDMA_INTR_MASK);
+       mask &= 0xf;
+       if (pi->port.line) mask <<= 8;
+       v &= ~mask;
+       MPSC_WRITE_M(pi, sdma_intr, SDMA_INTR_MASK, v);
+
+       if (pi->port.line) old >>= 8;
+       return old & 0xf;
+}
+
+static inline void
+mpsc_sdma_intr_unmask(mpsc_port_info_t *pi, u32 mask)
+{
+       u32     v;
+
+       DBG("mpsc_sdma_intr_unmask[%d]: clk_src: 0x%x\n", pi->port.line, mask);
+
+       v = MPSC_READ_M(pi, sdma_intr, SDMA_INTR_MASK);
+       mask &= 0xf;
+       if (pi->port.line) mask <<= 8;
+       v |= mask;
+       MPSC_WRITE_M(pi, sdma_intr, SDMA_INTR_MASK, v);
+       return;
+}
+
+static inline void
+mpsc_sdma_intr_ack(mpsc_port_info_t *pi)
+{
+       DBG("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line);
+       MPSC_WRITE(pi, sdma_intr, SDMA_INTR_CAUSE, 0);
+       return;
+}
+
+static inline void
+mpsc_sdma_set_rx_ring(mpsc_port_info_t *pi, mpsc_rx_desc_t *rxre_p)
+{
+       DBG("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n",
+               pi->port.line, (uint)rxre_p);
+
+       MPSC_WRITE(pi, sdma, SDMA_SCRDP, (u32)rxre_p);
+       return;
+}
+
+static inline void
+mpsc_sdma_set_tx_ring(mpsc_port_info_t *pi, volatile mpsc_tx_desc_t *txre_p)
+{
+       MPSC_WRITE(pi, sdma, SDMA_SFTDP, (int)txre_p);
+       MPSC_WRITE(pi, sdma, SDMA_SCTDP, (int)txre_p);
+       return;
+}
+
+static inline void
+mpsc_sdma_cmd(mpsc_port_info_t *pi, u32 val)
+{
+       u32     v;
+
+       v = MPSC_READ(pi, sdma, SDMA_SDCM);
+       if (val)
+               v |= val;
+       else
+               v = 0;
+       MPSC_WRITE(pi, sdma, SDMA_SDCM, v);
+       return;
+}
+
+static inline void
+mpsc_sdma_start_tx(mpsc_port_info_t *pi, volatile mpsc_tx_desc_t *txre_p)
+{
+       mpsc_sdma_set_tx_ring(pi, txre_p);
+       mpsc_sdma_cmd(pi, SDMA_SDCM_TXD);
+       return;
+}
+
+static inline void
+mpsc_sdma_stop(mpsc_port_info_t *pi)
+{
+       DBG("mpsc_sdma_stop[%d]: Stopping SDMA\n", pi->port.line);
+
+       /* Abort any SDMA transfers */
+       mpsc_sdma_cmd(pi, 0);
+       mpsc_sdma_cmd(pi, SDMA_SDCM_AR | SDMA_SDCM_AT);
+
+       /* Clear the SDMA current and first TX and RX pointers */
+       mpsc_sdma_set_tx_ring(pi, 0);
+       mpsc_sdma_set_rx_ring(pi, 0);
+       /* udelay(100); XXXX was in original gt64260 driver */
+
+       /* Disable interrupts */
+       mpsc_sdma_intr_mask(pi, 0xf);
+       mpsc_sdma_intr_ack(pi);
+        udelay(1000);
+
+       return;
+}
+
+/*
+ ******************************************************************************
+ *
+ * Multi-Protocol Serial Controller Routines (MPSC)
+ *
+ ******************************************************************************
+ */
+
+static void
+mpsc_hw_init(mpsc_port_info_t *pi)
+{
+       DBG("mpsc_hw_init[%d]: Initializing hardware\n", pi->port.line);
+
+       /* Set up clock routing */
+       MPSC_MOD_FIELD_M(pi, mpsc_routing, MPSC_MRR, 3, 0, 0);
+       MPSC_MOD_FIELD_M(pi, mpsc_routing, MPSC_MRR, 3, 6, 0);
+       MPSC_MOD_FIELD_M(pi, mpsc_routing, MPSC_RCRR, 4, 0, 0);
+       MPSC_MOD_FIELD_M(pi, mpsc_routing, MPSC_RCRR, 4, 8, 1);
+       MPSC_MOD_FIELD_M(pi, mpsc_routing, MPSC_TCRR, 4, 0, 0);
+       MPSC_MOD_FIELD_M(pi, mpsc_routing, MPSC_TCRR, 4, 8, 1);
+
+       /* Put MPSC in UART mode & enabel Tx/Rx egines */
+       MPSC_WRITE(pi, mpsc, MPSC_MMCRL, 0x000004c4);
+
+       /* No preamble, 16x divider, low-latency,  */
+       MPSC_WRITE(pi, mpsc, MPSC_MMCRH, 0x04400400);
+
+       MPSC_WRITE_M(pi, mpsc, MPSC_CHR_1, 0);
+       MPSC_WRITE_M(pi, mpsc, MPSC_CHR_2, 0);
+       MPSC_WRITE(pi, mpsc, MPSC_CHR_3, pi->mpsc_max_idle);
+       MPSC_WRITE(pi, mpsc, MPSC_CHR_4, 0);
+       MPSC_WRITE(pi, mpsc, MPSC_CHR_5, 0);
+       MPSC_WRITE(pi, mpsc, MPSC_CHR_6, 0);
+       MPSC_WRITE(pi, mpsc, MPSC_CHR_7, 0);
+       MPSC_WRITE(pi, mpsc, MPSC_CHR_8, 0);
+       MPSC_WRITE(pi, mpsc, MPSC_CHR_9, 0);
+       MPSC_WRITE(pi, mpsc, MPSC_CHR_10, 0);
+
+       return;
+}
+
+static inline void
+mpsc_enter_hunt(mpsc_port_info_t *pi)
+{
+       u32     v;
+
+       DBG("mpsc_enter_hunt[%d]: Hunting...\n", pi->port.line);
+
+       MPSC_MOD_FIELD_M(pi, mpsc, MPSC_CHR_2, 1, 31, 1);
+
+       if (pi->mirror_regs) {
+               udelay(100);
+       }
+       else
+               do {
+                       v = MPSC_READ_M(pi, mpsc, MPSC_CHR_2);
+               } while (v & MPSC_CHR_2_EH);
+
+       return;
+}
+
+static void
+mpsc_freeze(mpsc_port_info_t *pi)
+{
+       DBG("mpsc_freeze[%d]: Freezing\n", pi->port.line);
+
+       MPSC_MOD_FIELD_M(pi, mpsc, MPSC_MPCR, 1, 9, 1);
+       return;
+}
+
+static inline void
+mpsc_unfreeze(mpsc_port_info_t *pi)
+{
+       MPSC_MOD_FIELD_M(pi, mpsc, MPSC_MPCR, 1, 9, 0);
+
+       DBG("mpsc_unfreeze[%d]: Unfrozen\n", pi->port.line);
+       return;
+}
+
+static inline void
+mpsc_set_char_length(mpsc_port_info_t *pi, u32 len)
+{
+       DBG("mpsc_set_char_length[%d]: char len: %d\n", pi->port.line, len);
+
+       MPSC_MOD_FIELD_M(pi, mpsc, MPSC_MPCR, 2, 12, len);
+       return;
+}
+
+static inline void
+mpsc_set_stop_bit_length(mpsc_port_info_t *pi, u32 len)
+{
+       DBG("mpsc_set_stop_bit_length[%d]: stop bits: %d\n",pi->port.line,len);
+
+       MPSC_MOD_FIELD_M(pi, mpsc, MPSC_MPCR, 1, 14, len);
+       return;
+}
+
+static inline void
+mpsc_set_parity(mpsc_port_info_t *pi, u32 p)
+{
+       DBG("mpsc_set_parity[%d]: parity bits: 0x%x\n", pi->port.line, p);
+
+       MPSC_MOD_FIELD_M(pi, mpsc, MPSC_CHR_2, 2, 2, p);        /* TPM */
+       MPSC_MOD_FIELD_M(pi, mpsc, MPSC_CHR_2, 2, 18, p);       /* RPM */
+       return;
+}
+
+/*
+ ******************************************************************************
+ *
+ * Driver Init Routines
+ *
+ ******************************************************************************
+ */
+
+static void
+mpsc_init_hw(mpsc_port_info_t *pi)
+{
+       DBG("mpsc_init_hw[%d]: Initializing\n", pi->port.line);
+
+       mpsc_brg_init(pi, pi->brg_clk_src);
+       mpsc_brg_enable(pi);
+       mpsc_sdma_init(pi, dma_get_cache_alignment());/* burst a cacheline */
+       mpsc_sdma_stop(pi); 
+       mpsc_hw_init(pi);
+
+       return;
+}
+
+static int
+mpsc_alloc_ring_mem(mpsc_port_info_t *pi)
+{
+       int     rc = 0;
+       static void mpsc_free_ring_mem(mpsc_port_info_t *pi);
+
+       DBG("mpsc_alloc_ring_mem[%d]: Allocating ring mem\n", pi->port.line);
+
+       pi->desc_region_size = MPSC_TXR_SIZE + MPSC_RXR_SIZE +
+               (2 * MPSC_DESC_ALIGN);
+       pi->buf_region_size = MPSC_TXB_SIZE + MPSC_RXB_SIZE +
+               (2 * MPSC_BUF_ALIGN);
+
+       if (!pi->desc_region) {
+               if (!dma_supported(pi->port.dev, 0xffffffff)) {
+                       printk(KERN_ERR "MPSC: inadequate DMA support\n");
+                       rc = -ENXIO;
+               }
+               else if ((pi->desc_region = dma_alloc_coherent(pi->port.dev,
+                       pi->desc_region_size, &pi->desc_region_p,
+                       GFP_KERNEL)) == NULL) {
+
+                       printk(KERN_ERR "MPSC: can't alloc Desc region\n");
+                       rc = -ENOMEM;
+               }
+               else if ((pi->buf_region = kmalloc(pi->buf_region_size,
+                       GFP_KERNEL)) == NULL) {
+
+                       printk(KERN_ERR "MPSC: can't alloc bufs\n");
+                       mpsc_free_ring_mem(pi);
+                       rc = -ENOMEM;
+               }
+       }
+
+       return rc;
+}
+
+static void
+mpsc_free_ring_mem(mpsc_port_info_t *pi)
+{
+       DBG("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line);
+
+       if (pi->desc_region) {
+               MPSC_CACHE_INVALIDATE(pi, pi->desc_region,
+                       pi->desc_region + pi->desc_region_size);
+               dma_free_coherent(pi->port.dev, pi->desc_region_size,
+                       pi->desc_region, pi->desc_region_p);
+               pi->desc_region = NULL;
+               pi->desc_region_p = (dma_addr_t)NULL;
+       }
+
+       if (pi->buf_region) {
+               MPSC_CACHE_INVALIDATE(pi, pi->buf_region,
+                       pi->buf_region + pi->buf_region_size);
+               kfree(pi->buf_region);
+               pi->buf_region = NULL;
+       }
+
+       return;
+}
+
+static void
+mpsc_init_rings(mpsc_port_info_t *pi)
+{
+       mpsc_rx_desc_t  *rxre, *rxre_p;
+       mpsc_tx_desc_t  *txre, *txre_p;
+       u32             bp_p, save_first, i;
+       u8              *bp;
+
+       DBG("mpsc_init_rings[%d]: Initializing rings\n", pi->port.line);
+
+       BUG_ON((pi->desc_region == NULL) || (pi->buf_region == NULL));
+
+       memset(pi->desc_region, 0, pi->desc_region_size);
+       memset(pi->buf_region, 0, pi->buf_region_size);
+
+       pi->rxr = (mpsc_rx_desc_t *)ALIGN((u32)pi->desc_region,
+               (u32)MPSC_DESC_ALIGN);
+       pi->rxr_p = (mpsc_rx_desc_t *)ALIGN((u32)pi->desc_region_p,
+               (u32)MPSC_DESC_ALIGN);
+       pi->rxb = (u8 *)ALIGN((u32)pi->buf_region, (u32)MPSC_BUF_ALIGN);
+       pi->rxb_p = __pa(pi->rxb);
+
+       rxre = pi->rxr;
+       rxre_p = pi->rxr_p;
+       save_first = (u32)rxre_p;
+       bp = pi->rxb;
+       bp_p = pi->rxb_p;
+       for (i=0; i<MPSC_RXR_ENTRIES; i++,rxre++,rxre_p++) {
+               rxre->bufsize = cpu_to_be16(MPSC_RXBE_SIZE);
+               rxre->bytecnt = cpu_to_be16(0);
+               rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O |
+                       SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F |
+                       SDMA_DESC_CMDSTAT_L);
+               rxre->link = cpu_to_be32(rxre_p + 1);
+               rxre->buf_ptr = cpu_to_be32(bp_p);
+               MPSC_CACHE_FLUSH(pi, rxre, rxre + 1);
+               dma_map_single(pi->port.dev, bp, MPSC_RXBE_SIZE,
+                       DMA_FROM_DEVICE);
+               MPSC_CACHE_INVALIDATE(pi, bp, bp + MPSC_RXBE_SIZE);
+               bp += MPSC_RXBE_SIZE;
+               bp_p += MPSC_RXBE_SIZE;
+       }
+       (--rxre)->link = cpu_to_be32(save_first); /* Wrap last back to first */
+       MPSC_CACHE_FLUSH(pi, rxre, rxre + 1);
+
+       pi->txr = (mpsc_tx_desc_t *)ALIGN((u32)&pi->rxr[MPSC_RXR_ENTRIES],
+               (u32)MPSC_DESC_ALIGN);
+       pi->txr_p = (mpsc_tx_desc_t *)ALIGN((u32)&pi->rxr_p[MPSC_RXR_ENTRIES],
+               (u32)MPSC_DESC_ALIGN);
+       pi->txb = (u8 *)ALIGN((u32)(pi->rxb + MPSC_RXB_SIZE),
+               (u32)MPSC_BUF_ALIGN);
+       pi->txb_p = __pa(pi->txb);
+
+       txre = pi->txr;
+       txre_p = pi->txr_p;
+       save_first = (u32)txre_p;
+       bp = pi->txb;
+       bp_p = pi->txb_p;
+       for (i=0; i<MPSC_TXR_ENTRIES; i++,txre++,txre_p++) {
+               txre->link = cpu_to_be32(txre_p + 1);
+               txre->buf_ptr = cpu_to_be32(bp_p);
+               MPSC_CACHE_FLUSH(pi, txre, txre + 1);
+               dma_map_single(pi->port.dev, bp, MPSC_TXBE_SIZE, DMA_TO_DEVICE);
+               bp += MPSC_TXBE_SIZE;
+               bp_p += MPSC_TXBE_SIZE;
+       }
+       (--txre)->link = cpu_to_be32(save_first); /* Wrap last back to first */
+       MPSC_CACHE_FLUSH(pi, txre, txre + 1);
+
+       return;
+}
+
+static void
+mpsc_uninit_rings(mpsc_port_info_t *pi)
+{
+       u32             bp_p, i;
+
+       DBG("mpsc_uninit_rings[%d]: Uninitializing rings\n", pi->port.line);
+
+       BUG_ON((pi->desc_region == NULL) || (pi->buf_region == NULL));
+
+       bp_p = pi->rxb_p;
+       for (i=0; i<MPSC_RXR_ENTRIES; i++) {
+               dma_unmap_single(pi->port.dev, bp_p, MPSC_RXBE_SIZE,
+                       DMA_FROM_DEVICE);
+               bp_p += MPSC_RXBE_SIZE;
+       }
+       pi->rxr = NULL;
+       pi->rxr_p = NULL;
+       pi->rxr_posn = 0;
+       pi->rxb = NULL;
+       pi->rxb_p = 0;
+
+       bp_p = pi->txb_p;
+       for (i=0; i<MPSC_TXR_ENTRIES; i++) {
+               dma_unmap_single(pi->port.dev, bp_p, MPSC_TXBE_SIZE,
+                       DMA_TO_DEVICE);
+               bp_p += MPSC_TXBE_SIZE;
+       }
+       pi->txr = NULL;
+       pi->txr_p = NULL;
+       pi->txr_posn = 0;
+       pi->txb = NULL;
+       pi->txb_p = 0;
+
+       return;
+}
+
+static int
+mpsc_make_ready(mpsc_port_info_t *pi)
+{
+       int     rc;
+
+       DBG("mpsc_make_ready[%d]: Making cltr ready\n", pi->port.line);
+
+       if (!pi->ready) {
+               mpsc_init_hw(pi);
+               if ((rc = mpsc_alloc_ring_mem(pi)))
+                       return rc;
+               mpsc_init_rings(pi);
+               pi->ready = 1;
+       }
+
+       return 0;
+}
+
+/*
+ ******************************************************************************
+ *
+ * Interrupt Handling Routines
+ *
+ ******************************************************************************
+ */
+
+static inline void
+mpsc_rx_intr(mpsc_port_info_t *pi, struct pt_regs *regs)
+{
+       volatile mpsc_rx_desc_t *rxre = &pi->rxr[pi->rxr_posn];
+       struct tty_struct       *tty = pi->port.info->tty;
+       u32                     cmdstat, bytes_in;
+       u8                      *bp;
+       dma_addr_t              bp_p;
+       static void mpsc_start_rx(mpsc_port_info_t *pi);
+
+       DBG("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line);
+
+       /*
+        * Loop through Rx descriptors handling ones that have been completed.
+        */
+       MPSC_CACHE_INVALIDATE(pi, rxre, rxre + 1);
+
+       while (!((cmdstat = be32_to_cpu(rxre->cmdstat)) & SDMA_DESC_CMDSTAT_O)){
+               bytes_in = be16_to_cpu(rxre->bytecnt);
+
+               if (unlikely((tty->flip.count + bytes_in) >= TTY_FLIPBUF_SIZE)){
+                       tty->flip.work.func((void *)tty);
+
+                       if ((tty->flip.count + bytes_in) >= TTY_FLIPBUF_SIZE) {
+                               /* Take what we can, throw away the rest */
+                               bytes_in = TTY_FLIPBUF_SIZE - tty->flip.count;
+                               cmdstat &= ~SDMA_DESC_CMDSTAT_PE;
+                       }
+               }
+
+               bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE);
+               bp_p = pi->txb_p + (pi->rxr_posn * MPSC_RXBE_SIZE);
+
+               dma_sync_single_for_cpu(pi->port.dev, bp_p, MPSC_RXBE_SIZE,
+                       DMA_FROM_DEVICE);
+               MPSC_CACHE_INVALIDATE(pi, bp, bp + MPSC_RXBE_SIZE);
+
+               /*
+                * Other than for parity error, the manual provides little
+                * info on what data will be in a frame flagged by any of
+                * these errors.  For parity error, it is the last byte in
+                * the buffer that had the error.  As for the rest, I guess
+                * we'll assume there is no data in the buffer.
+                * If there is...it gets lost.
+                */
+               if (cmdstat & (SDMA_DESC_CMDSTAT_BR | SDMA_DESC_CMDSTAT_FR |
+                       SDMA_DESC_CMDSTAT_OR)) {
+
+                       pi->port.icount.rx++;
+
+                       if (cmdstat & SDMA_DESC_CMDSTAT_BR) { /* Break */
+                               pi->port.icount.brk++;
+
+                               if (uart_handle_break(&pi->port))
+                                       goto next_frame;
+                       }
+                       else if (cmdstat & SDMA_DESC_CMDSTAT_FR) /* Framing */
+                               pi->port.icount.frame++;
+                       else if (cmdstat & SDMA_DESC_CMDSTAT_OR) /* Overrun */
+                               pi->port.icount.overrun++;
+
+                       cmdstat &= pi->port.read_status_mask;
+
+                       if (!(cmdstat & pi->port.ignore_status_mask)) {
+                               if (cmdstat & SDMA_DESC_CMDSTAT_BR)
+                                       *tty->flip.flag_buf_ptr = TTY_BREAK;
+                               else if (cmdstat & SDMA_DESC_CMDSTAT_FR)
+                                       *tty->flip.flag_buf_ptr = TTY_FRAME;
+                               else if (cmdstat & SDMA_DESC_CMDSTAT_OR)
+                                       *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+
+                               tty->flip.flag_buf_ptr++;
+                               *tty->flip.char_buf_ptr = '\0';
+                               tty->flip.char_buf_ptr++;
+                               tty->flip.count++;
+                       }
+               }
+               else {
+                       if (uart_handle_sysrq_char(&pi->port, *bp, regs)) {
+                               bp++;
+                               bytes_in--;
+                       }
+
+                       memcpy(tty->flip.char_buf_ptr, bp, bytes_in);
+                       memset(tty->flip.flag_buf_ptr, TTY_NORMAL, bytes_in);
+
+                       tty->flip.char_buf_ptr += bytes_in;
+                       tty->flip.flag_buf_ptr += bytes_in;
+                       tty->flip.count += bytes_in;
+                       pi->port.icount.rx += bytes_in;
+
+                       cmdstat &= SDMA_DESC_CMDSTAT_PE;
+
+                       if (cmdstat) {  /* Parity */
+                               pi->port.icount.parity++;
+
+                               if (!(cmdstat & pi->port.read_status_mask))
+                                       *(tty->flip.flag_buf_ptr-1) = TTY_FRAME;
+                       }
+               }
+
+next_frame:
+               dma_sync_single_for_device(pi->port.dev, bp_p,
+                       MPSC_RXBE_SIZE, DMA_FROM_DEVICE);
+               rxre->bytecnt = cpu_to_be16(0);
+               wmb(); /* ensure other writes done before cmdstat update */
+               rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O |
+                       SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F |
+                       SDMA_DESC_CMDSTAT_L);
+               MPSC_CACHE_FLUSH(pi, rxre, rxre + 1);
+
+               /* Advance to next descriptor */
+               pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1);
+               rxre = &pi->rxr[pi->rxr_posn];
+               MPSC_CACHE_INVALIDATE(pi, rxre, rxre + 1);
+       }
+
+       /* Restart rx engine, if its stopped */
+       if ((MPSC_READ(pi, sdma, SDMA_SDCM) & SDMA_SDCM_ERD) == 0) {
+               mpsc_start_rx(pi);
+       }
+
+       tty_flip_buffer_push(tty);
+       return;
+}
+
+static inline void
+mpsc_send_tx_data(mpsc_port_info_t *pi, volatile mpsc_tx_desc_t *txre,
+       volatile mpsc_tx_desc_t *txre_p, void *bp, u32 count, u32 intr)
+{
+       dma_sync_single_for_device(pi->port.dev, be32_to_cpu(txre->buf_ptr),
+                       MPSC_TXBE_SIZE, DMA_TO_DEVICE);
+       MPSC_CACHE_FLUSH(pi, bp, bp + MPSC_TXBE_SIZE);
+
+       txre->bytecnt = cpu_to_be16(count);
+       txre->shadow = txre->bytecnt;
+       wmb(); /* ensure cmdstat is last field updated */
+       txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F |
+               SDMA_DESC_CMDSTAT_L | ((intr) ? SDMA_DESC_CMDSTAT_EI : 0));
+       MPSC_CACHE_FLUSH(pi, txre, txre + 1);
+
+       /* Start Tx engine, if its stopped */
+       if ((MPSC_READ(pi, sdma, SDMA_SDCM) & SDMA_SDCM_TXD) == 0) {
+               mpsc_sdma_start_tx(pi, txre_p);
+       }
+
+       return;
+}
+
+static inline void
+mpsc_tx_intr(mpsc_port_info_t *pi)
+{
+       volatile mpsc_tx_desc_t *txre = &pi->txr[pi->txr_posn];
+       volatile mpsc_tx_desc_t *txre_p = &pi->txr_p[pi->txr_posn];
+       struct circ_buf         *xmit = &pi->port.info->xmit;
+       u8                      *bp;
+       u32                     i;
+
+       MPSC_CACHE_INVALIDATE(pi, txre, txre + 1);
+
+       while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) {
+               bp = &pi->txb[pi->txr_posn * MPSC_TXBE_SIZE];
+
+               dma_sync_single_for_cpu(pi->port.dev,be32_to_cpu(txre->buf_ptr),
+                       MPSC_TXBE_SIZE, DMA_TO_DEVICE);
+
+               if (pi->port.x_char) {
+                       /*
+                        * Ideally, we should use the TCS field in CHR_1 to
+                        * put the x_char out immediately but errata prevents
+                        * us from being able to read CHR_2 to know that its
+                        * safe to write to CHR_1.  Instead, just put it
+                        * in-band with all the other Tx data.
+                        */
+                       *bp = pi->port.x_char;
+                       pi->port.x_char = 0;
+                       i = 1;
+               }
+               else if (!uart_circ_empty(xmit) && !uart_tx_stopped(&pi->port)){
+                       i = MIN(MPSC_TXBE_SIZE, uart_circ_chars_pending(xmit));
+                       i = MIN(i, CIRC_CNT_TO_END(xmit->head, xmit->tail,
+                                                       UART_XMIT_SIZE));
+                       memcpy(bp, &xmit->buf[xmit->tail], i);
+                       xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1);
+               }
+               else { /* No more data to transmit or tx engine is stopped */
+                       MPSC_CACHE_INVALIDATE(pi, txre, txre + 1);
+                       return;
+               }
+
+               mpsc_send_tx_data(pi, txre, txre_p, bp, i, 1);
+               pi->port.icount.tx += i;
+
+               if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+                       uart_write_wakeup(&pi->port);
+
+               /* Advance to next descriptor */
+               pi->txr_posn = (pi->txr_posn + 1) & (MPSC_TXR_ENTRIES - 1);
+               txre = &pi->txr[pi->txr_posn];
+               txre_p = &pi->txr_p[pi->txr_posn];
+               MPSC_CACHE_INVALIDATE(pi, txre, txre + 1);
+       }
+
+       return;
+}
+
+/*
+ * This is the driver's interrupt handler.  To avoid a race, we first clear
+ * the interrupt, then handle any completed Rx/Tx descriptors.  When done
+ * handling those descriptors, we restart the Rx/Tx engines if they're stopped.
+ */
+static irqreturn_t
+mpsc_sdma_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+       mpsc_port_info_t        *pi = dev_id;
+       ulong                   iflags;
+
+       DBG("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n", pi->port.line);
+
+       spin_lock_irqsave(&pi->port.lock, iflags);
+       mpsc_sdma_intr_ack(pi);
+       mpsc_rx_intr(pi, regs);
+       mpsc_tx_intr(pi);
+       spin_unlock_irqrestore(&pi->port.lock, iflags);
+
+       DBG("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line);
+       return IRQ_HANDLED;
+}
+
+/*
+ ******************************************************************************
+ *
+ * serial_core.c Interface routines
+ *
+ ******************************************************************************
+ */
+
+static uint
+_mpsc_tx_empty(mpsc_port_info_t *pi)
+{
+       return (((MPSC_READ(pi, sdma, SDMA_SDCM) & SDMA_SDCM_TXD) == 0) ?
+                                                       TIOCSER_TEMT : 0);
+}
+
+static uint
+mpsc_tx_empty(struct uart_port *port)
+{
+       mpsc_port_info_t        *pi = (mpsc_port_info_t *)port;
+       ulong                   iflags;
+       uint                    rc;
+
+       spin_lock_irqsave(&pi->port.lock, iflags);
+       rc = _mpsc_tx_empty(pi);
+       spin_unlock_irqrestore(&pi->port.lock, iflags);
+
+       return rc;
+}
+
+static void
+mpsc_set_mctrl(struct uart_port *port, uint mctrl)
+{
+       /* Have no way to set modem control lines AFAICT */
+       return;
+}
+
+static uint
+mpsc_get_mctrl(struct uart_port *port)
+{
+       mpsc_port_info_t        *pi = (mpsc_port_info_t *)port;
+       u32                     mflags, status;
+       ulong                   iflags;
+
+       spin_lock_irqsave(&pi->port.lock, iflags);
+       status = MPSC_READ_M(pi, mpsc, MPSC_CHR_10);
+       spin_unlock_irqrestore(&pi->port.lock, iflags);
+
+       mflags = 0;
+       if (status & 0x1)
+               mflags |= TIOCM_CTS;
+       if (status & 0x2)
+               mflags |= TIOCM_CAR;
+
+       return mflags | TIOCM_DSR;      /* No way to tell if DSR asserted */
+}
+
+static void
+mpsc_stop_tx(struct uart_port *port, uint tty_start)
+{
+       mpsc_port_info_t *pi = (mpsc_port_info_t *)port;
+
+       DBG("mpsc_stop_tx[%d]: tty_start: %d\n", port->line, tty_start);
+
+       mpsc_freeze(pi);
+       return;
+}
+
+static void
+mpsc_start_tx(struct uart_port *port, uint tty_start)
+{
+       mpsc_port_info_t *pi = (mpsc_port_info_t *)port;
+
+       mpsc_unfreeze(pi);
+       mpsc_tx_intr(pi); /* Load Tx data into Tx ring bufs & go */
+
+       DBG("mpsc_start_tx[%d]: tty_start: %d\n", port->line, tty_start);
+       return;
+}
+
+static void
+mpsc_start_rx(mpsc_port_info_t *pi)
+{
+       DBG("mpsc_start_rx[%d]: Starting...\n", pi->port.line);
+
+       if (pi->rcv_data) {
+               mb();
+               mpsc_enter_hunt(pi);
+               mpsc_sdma_cmd(pi, SDMA_SDCM_ERD);
+       }
+       return;
+}
+
+static void
+mpsc_stop_rx(struct uart_port *port)
+{
+       mpsc_port_info_t *pi = (mpsc_port_info_t *)port;
+
+       DBG("mpsc_stop_rx[%d]: Stopping...\n", port->line);
+
+       mpsc_sdma_cmd(pi, SDMA_SDCM_AR);
+       return;
+}
+
+static void
+mpsc_enable_ms(struct uart_port *port)
+{
+       return;         /* Not supported */
+}
+
+static void
+mpsc_break_ctl(struct uart_port *port, int ctl)
+{
+       mpsc_port_info_t        *pi = (mpsc_port_info_t *)port;
+       ulong                   flags;
+
+       spin_lock_irqsave(&pi->port.lock, flags);
+       if (ctl) {
+               /* Send as many BRK chars as we can */
+               MPSC_WRITE_M(pi, mpsc, MPSC_CHR_1, 0x00ff0000);
+       }
+       else {
+               /* Stop sending BRK chars */
+               MPSC_WRITE_M(pi, mpsc, MPSC_CHR_1, 0);
+       }
+       spin_unlock_irqrestore(&pi->port.lock, flags);
+
+       return;
+}
+
+static int
+mpsc_startup(struct uart_port *port)
+{
+       mpsc_port_info_t        *pi = (mpsc_port_info_t *)port;
+       int                     rc;
+
+       DBG("mpsc_startup[%d]: Starting up MPSC, irq: %d\n",
+               port->line, pi->port.irq);
+
+       if ((rc = mpsc_make_ready(pi)) == 0) {
+               /* Setup IRQ handler */
+               mpsc_sdma_intr_ack(pi);
+               mpsc_sdma_intr_unmask(pi, 0xf);
+
+               if (request_irq(pi->port.irq, mpsc_sdma_intr, 0, "MPSC/SDMA",
+                                                                       pi)) {
+                       printk(KERN_ERR "MPSC: Can't get SDMA IRQ");
+                       printk("MPSC: Can't get SDMA IRQ %d\n", pi->port.irq);
+               }
+
+               mpsc_sdma_set_rx_ring(pi, &pi->rxr_p[pi->rxr_posn]);
+               mpsc_start_rx(pi);
+       }
+
+       return rc;
+}
+
+static void
+mpsc_shutdown(struct uart_port *port)
+{
+       mpsc_port_info_t *pi = (mpsc_port_info_t *)port;
+       static void mpsc_release_port(struct uart_port *port);
+
+       DBG("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line);
+
+       mpsc_sdma_stop(pi);
+       free_irq(pi->port.irq, pi);
+       return;
+}
+
+static void
+mpsc_set_termios(struct uart_port *port, struct termios *termios,
+                      struct termios *old)
+{
+       mpsc_port_info_t        *pi = (mpsc_port_info_t *)port;
+       u32                     baud, quot;
+       ulong                   flags;
+       u32                     chr_bits, stop_bits, par;
+
+       pi->c_iflag = termios->c_iflag;
+       pi->c_cflag = termios->c_cflag;
+
+       switch (termios->c_cflag & CSIZE) {
+               case CS5:
+                       chr_bits = MPSC_MPCR_CL_5;
+                       break;
+               case CS6:
+                       chr_bits = MPSC_MPCR_CL_6;
+                       break;
+               case CS7:
+                       chr_bits = MPSC_MPCR_CL_7;
+                       break;
+               default:
+               case CS8:
+                       chr_bits = MPSC_MPCR_CL_8;
+                       break;
+       }
+
+       if (termios->c_cflag & CSTOPB)
+               stop_bits = MPSC_MPCR_SBL_2;
+       else
+               stop_bits = MPSC_MPCR_SBL_1;
+
+       if (termios->c_cflag & PARENB) {
+               if (termios->c_cflag & PARODD)
+                       par = MPSC_CHR_2_PAR_ODD;
+               else
+                       par = MPSC_CHR_2_PAR_EVEN;
+#ifdef CMSPAR
+               if (termios->c_cflag & CMSPAR) {
+                       if (termios->c_cflag & PARODD)
+                               par = MPSC_CHR_2_PAR_MARK;
+                       else
+                               par = MPSC_CHR_2_PAR_SPACE;
+               }
+#endif
+       }
+
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk); 
+       quot = uart_get_divisor(port, baud);
+
+       spin_lock_irqsave(&pi->port.lock, flags);
+
+       uart_update_timeout(port, termios->c_cflag, baud);
+
+       mpsc_set_char_length(pi, chr_bits);
+       mpsc_set_stop_bit_length(pi, stop_bits);
+       mpsc_set_parity(pi, par);
+       mpsc_set_baudrate(pi, baud);
+
+       /* Characters/events to read */
+       pi->rcv_data = 1;
+       pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR;
+
+       if (termios->c_iflag & INPCK)
+               pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE |
+                       SDMA_DESC_CMDSTAT_FR;
+
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR;
+
+       /* Characters/events to ignore */
+       pi->port.ignore_status_mask = 0;
+
+       if (termios->c_iflag & IGNPAR)
+               pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE |
+                       SDMA_DESC_CMDSTAT_FR;
+
+       if (termios->c_iflag & IGNBRK) {
+               pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR;
+
+               if (termios->c_iflag & IGNPAR)
+                       pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR;
+       }
+
+       /* Ignore all chars if CREAD not set */
+       if (!(termios->c_cflag & CREAD))
+               pi->rcv_data = 0;
+
+       spin_unlock_irqrestore(&pi->port.lock, flags);
+       return;
+}
+
+static const char *
+mpsc_type(struct uart_port *port)
+{
+       DBG("mpsc_type[%d]: port type: %s\n", port->line, MPSC_DRIVER_NAME);
+       return MPSC_DRIVER_NAME;
+}
+
+static int
+mpsc_request_port(struct uart_port *port)
+{
+       /* Should make chip/platform specific call */
+       return 0;
+}
+
+static void
+mpsc_release_port(struct uart_port *port)
+{
+       mpsc_port_info_t *pi = (mpsc_port_info_t *)port;
+
+       mpsc_uninit_rings(pi);
+       mpsc_free_ring_mem(pi);
+       pi->ready = 0;
+
+       return;
+}
+
+static void
+mpsc_config_port(struct uart_port *port, int flags)
+{
+       return;
+}
+
+static int
+mpsc_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       mpsc_port_info_t *pi = (mpsc_port_info_t *)port;
+       int     rc = 0;
+
+       DBG("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line);
+
+       if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC)
+               rc = -EINVAL;
+       if (pi->port.irq != ser->irq)
+               rc = -EINVAL;
+       if (ser->io_type != SERIAL_IO_MEM)
+               rc = -EINVAL;
+       if (pi->port.uartclk / 16 != ser->baud_base) /* XXXX Not sure */
+               rc = -EINVAL;
+       if ((void *)pi->port.mapbase != ser->iomem_base)
+               rc = -EINVAL;
+       if (pi->port.iobase != ser->port)
+               rc = -EINVAL;
+       if (ser->hub6 != 0)
+               rc = -EINVAL;
+
+       return rc;
+}
+
+static struct uart_ops mpsc_pops = {
+       .tx_empty       = mpsc_tx_empty,
+       .set_mctrl      = mpsc_set_mctrl,
+       .get_mctrl      = mpsc_get_mctrl,
+       .stop_tx        = mpsc_stop_tx,
+       .start_tx       = mpsc_start_tx,
+       .stop_rx        = mpsc_stop_rx,
+       .enable_ms      = mpsc_enable_ms,
+       .break_ctl      = mpsc_break_ctl,
+       .startup        = mpsc_startup,
+       .shutdown       = mpsc_shutdown,
+       .set_termios    = mpsc_set_termios,
+       .type           = mpsc_type,
+       .release_port   = mpsc_release_port,
+       .request_port   = mpsc_request_port,
+       .config_port    = mpsc_config_port,
+       .verify_port    = mpsc_verify_port,
+};
+
+/*
+ ******************************************************************************
+ *
+ * Console Interface Routines
+ *
+ ******************************************************************************
+ */
+
+#ifdef CONFIG_SERIAL_MPSC_CONSOLE
+static void
+mpsc_console_write(struct console *co, const char *s, uint count)
+{
+       mpsc_port_info_t        *pi = &mpsc_ports[co->index];
+       volatile mpsc_tx_desc_t *txre = &pi->txr[pi->txr_posn];
+       volatile mpsc_tx_desc_t *txre_p = &pi->txr_p[pi->txr_posn];
+       u8                      *bp, *dp, add_cr = 0;
+       int                     i;
+
+       /*
+        * Step thru tx ring one entry at a time, filling up its buf, sending
+        * the data out and moving to the next ring entry until its all out.
+        */
+       MPSC_CACHE_INVALIDATE(pi, txre, txre + 1);
+
+       while (count > 0) {
+               while (_mpsc_tx_empty(pi) != TIOCSER_TEMT);
+
+               BUG_ON(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O);
+
+               bp = dp = &pi->txb[pi->txr_posn * MPSC_TXBE_SIZE];
+
+               dma_sync_single_for_cpu(pi->port.dev,be32_to_cpu(txre->buf_ptr),
+                       MPSC_TXBE_SIZE, DMA_TO_DEVICE);
+
+               for (i=0; i<MPSC_TXBE_SIZE; i++) {
+                       if (count == 0)
+                               break;
+
+                       if (add_cr) {
+                               *(dp++) = '\r';
+                               add_cr = 0;
+                       }
+                       else {
+                               *(dp++) = *s;
+
+                               if (*(s++) == '\n') { /* add '\r' after '\n' */
+                                       add_cr = 1;
+                                       count++;
+                               }
+                       }
+
+                       count--;
+               }
+
+               mpsc_send_tx_data(pi, txre, txre_p, bp, i, 0);
+
+               /* Advance to next descriptor */
+               pi->txr_posn = (pi->txr_posn + 1) & (MPSC_TXR_ENTRIES - 1);
+               txre = &pi->txr[pi->txr_posn];
+               txre_p = &pi->txr_p[pi->txr_posn];
+               MPSC_CACHE_INVALIDATE(pi, txre, txre + 1);
+       }
+
+       while (_mpsc_tx_empty(pi) != TIOCSER_TEMT);
+       return;
+}
+
+static int __init
+mpsc_console_setup(struct console *co, char *options)
+{
+       mpsc_port_info_t        *pi;
+       int                     baud, bits, parity, flow;
+
+       DBG("mpsc_console_setup[%d]: options: %s\n", co->index, options);
+
+       if (co->index >= MPSC_NUM_CTLRS)
+               co->index = 0;
+
+       pi = &mpsc_ports[co->index];
+
+       baud = pi->default_baud;
+       bits = pi->default_bits;
+       parity = pi->default_parity;
+       flow = pi->default_flow;
+
+       if (!pi->port.ops)
+               return -ENODEV;
+
+       spin_lock_init(&pi->port.lock); /* Temporary fix--copied from 8250.c */
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(&pi->port, co, baud, parity, bits, flow);
+}
+
+extern struct uart_driver mpsc_reg;
+static struct console mpsc_console = {
+       .name           = MPSC_DEV_NAME,
+       .write          = mpsc_console_write,
+       .device         = uart_console_device,
+       .setup          = mpsc_console_setup,
+       .flags          = CON_PRINTBUFFER,
+       .index          = -1,
+       .data           = &mpsc_reg,
+};
+
+static int __init
+mpsc_console_init(void)
+{
+       DBG("mpsc_console_init: Enter\n");
+       register_console(&mpsc_console);
+       return 0;
+}
+console_initcall(mpsc_console_init);
+
+static int __init
+mpsc_late_console_init(void)
+{
+       DBG("mpsc_late_console_init: Enter\n");
+
+       if (!(mpsc_console.flags & CON_ENABLED))
+               register_console(&mpsc_console);
+       return 0;
+}
+late_initcall(mpsc_late_console_init);
+
+#define MPSC_CONSOLE   &mpsc_console
+#else
+#define MPSC_CONSOLE   NULL
+#endif
+
+/*
+ ******************************************************************************
+ *
+ * Driver Interface Routines
+ *
+ ******************************************************************************
+ */
+
+static void
+mpsc_map_regs(mpsc_port_info_t *pi)
+{
+       pi->mpsc_base = (u32)ioremap(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE);
+       pi->mpsc_routing_base = (u32)ioremap(pi->mpsc_routing_base_p,
+                                               MPSC_ROUTING_REG_BLOCK_SIZE);
+       pi->sdma_base = (u32)ioremap(pi->sdma_base_p, SDMA_REG_BLOCK_SIZE);
+       pi->sdma_intr_base = (u32)ioremap(pi->sdma_intr_base_p,
+                                               SDMA_INTR_REG_BLOCK_SIZE);
+       pi->brg_base = (u32)ioremap(pi->brg_base_p, BRG_REG_BLOCK_SIZE);
+
+       return;
+}
+
+static void
+mpsc_unmap_regs(mpsc_port_info_t *pi)
+{
+       iounmap((void *)pi->mpsc_base);
+       iounmap((void *)pi->mpsc_routing_base);
+       iounmap((void *)pi->sdma_base);
+       iounmap((void *)pi->sdma_intr_base);
+       iounmap((void *)pi->brg_base);
+
+       pi->mpsc_base = 0;
+       pi->mpsc_routing_base = 0;
+       pi->sdma_base = 0;
+       pi->sdma_intr_base = 0;
+       pi->brg_base = 0;
+
+       return;
+}
+
+/* Called from platform specific device probe routine */
+mpsc_port_info_t *
+mpsc_device_probe(int index)
+{
+       mpsc_port_info_t        *pi = NULL;
+
+       if ((index >= 0) && (index < MPSC_NUM_CTLRS))
+               pi = &mpsc_ports[index];
+
+       return pi;
+}
+
+/* Called from platform specific device remove routine */
+mpsc_port_info_t *
+mpsc_device_remove(int index)
+{
+       mpsc_port_info_t        *pi = NULL;
+
+       if ((index >= 0) && (index < MPSC_NUM_CTLRS))
+               pi = &mpsc_ports[index];
+
+       return pi;
+}
+
+static struct uart_driver mpsc_reg = {
+       .owner                  = THIS_MODULE,
+       .driver_name            = MPSC_DRIVER_NAME,
+       .devfs_name             = MPSC_DEVFS_NAME,
+       .dev_name               = MPSC_DEV_NAME,
+       .major                  = MPSC_MAJOR,
+       .minor                  = MPSC_MINOR_START,
+       .nr                     = MPSC_NUM_CTLRS,
+       .cons                   = MPSC_CONSOLE,
+};
+
+static int __init
+mpsc_init(void)
+{
+       mpsc_port_info_t        *pi;
+       int                     i, j, rc;
+
+       printk(KERN_INFO "Serial: MPSC driver $Revision: 1.00 $\n");
+
+       if ((rc = mpsc_platform_register_driver()) >= 0) {
+               if ((rc = uart_register_driver(&mpsc_reg)) < 0) {
+                       mpsc_platform_unregister_driver();
+               }
+               else {
+                       for (i=0; i<MPSC_NUM_CTLRS; i++) {
+                               pi = &mpsc_ports[i];
+
+                               pi->port.line = i;
+                               pi->port.type = PORT_MPSC;
+                               pi->port.fifosize = MPSC_TXBE_SIZE;
+                               pi->port.membase = (char *)pi->mpsc_base;
+                               pi->port.mapbase = (ulong)pi->mpsc_base;
+                               pi->port.ops = &mpsc_pops;
+
+                               mpsc_map_regs(pi);
+
+                               if ((rc = mpsc_make_ready(pi)) >= 0) {
+                                       uart_add_one_port(&mpsc_reg, &pi->port);
+                               }
+                               else { /* on failure, undo everything */
+                                       for (j=0; j<i; j++) {
+                                               mpsc_unmap_regs(&mpsc_ports[j]);
+                                               uart_remove_one_port(&mpsc_reg,
+                                                       &mpsc_ports[j].port);
+                                       }
+
+                                       uart_unregister_driver(&mpsc_reg);
+                                       mpsc_platform_unregister_driver();
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return rc;
+}
+
+static void __exit
+mpsc_exit(void)
+{
+       int     i;
+
+       DBG("mpsc_exit: Exiting\n");
+
+       for (i=0; i<MPSC_NUM_CTLRS; i++) {
+               mpsc_unmap_regs(&mpsc_ports[i]);
+               uart_remove_one_port(&mpsc_reg, &mpsc_ports[i].port);
+       }
+
+       uart_unregister_driver(&mpsc_reg);
+       mpsc_platform_unregister_driver();
+
+       return;
+}
+
+int
+register_serial(struct serial_struct *req)
+{
+       return uart_register_port(&mpsc_reg, &mpsc_ports[req->line].port);
+}
+
+void
+unregister_serial(int line)
+{
+       uart_unregister_port(&mpsc_reg, line);
+       return;
+}
+
+module_init(mpsc_init);
+module_exit(mpsc_exit);
+
+EXPORT_SYMBOL(register_serial);
+EXPORT_SYMBOL(unregister_serial);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver $Revision: 1.00 $");
+MODULE_VERSION(MPSC_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(MPSC_MAJOR);
diff --git a/drivers/serial/mpsc/mpsc.h b/drivers/serial/mpsc/mpsc.h
new file mode 100644 (file)
index 0000000..400629e
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * drivers/serial/mpsc/mpsc.h
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2004 (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        __MPSC_H__
+#define        __MPSC_H__
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+#include "mpsc_defs.h"
+
+
+/*
+ * Descriptors and buffers must be cache line aligned.
+ * Buffers lengths must be multiple of cache line size.
+ * Number of Tx & Rx descriptors must be power of 2.
+ */
+#define        MPSC_DESC_ALIGN         dma_get_cache_alignment()
+#define MPSC_BUF_ALIGN         dma_get_cache_alignment()
+
+#define        MPSC_RXR_ENTRIES        32
+#define        MPSC_RXRE_SIZE          sizeof(mpsc_rx_desc_t)
+#define        MPSC_RXR_SIZE           (MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE)
+#define        MPSC_RXBE_SIZE          dma_get_cache_alignment()
+#define        MPSC_RXB_SIZE           (MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE)
+
+#define        MPSC_TXR_ENTRIES        32
+#define        MPSC_TXRE_SIZE          sizeof(mpsc_tx_desc_t)
+#define        MPSC_TXR_SIZE           (MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE)
+#define        MPSC_TXBE_SIZE          dma_get_cache_alignment()
+#define        MPSC_TXB_SIZE           (MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE)
+
+typedef struct {
+        u16   bufsize;
+       u16   bytecnt;
+        u32   cmdstat;
+        u32   link;
+        u32   buf_ptr;
+} mpsc_rx_desc_t __attribute((packed));
+
+/* Tx and Rx Ring entry descriptors */
+typedef struct {
+        u16   bytecnt;
+        u16   shadow;
+        u32   cmdstat;
+        u32   link;
+        u32   buf_ptr;
+} mpsc_tx_desc_t __attribute((packed));
+
+/* The main driver data structure */
+typedef struct {
+       struct uart_port        port;   /* Overlay uart_port structure */
+
+       /* Internal driver state for this ctlr */
+       u8                      ready;
+       u8                      rcv_data;
+       tcflag_t                c_iflag;        /* save termios->c_iflag */
+       tcflag_t                c_cflag;        /* save termios->c_cflag */
+
+       /* Info passed in from platform */
+       u8                      mirror_regs;    /* Need to mirror regs? */
+       u8                      cache_mgmt;     /* Need manual cache mgmt? */
+       u8                      brg_can_tune;   /* BRG has baud tuning? */
+       u32                     brg_clk_src;
+       u16                     mpsc_max_idle;
+       int                     default_baud;
+       int                     default_bits;
+       int                     default_parity;
+       int                     default_flow;
+
+       /* Physical addresses of various blocks of registers (from platform) */
+       u32                     mpsc_base_p;
+       u32                     mpsc_routing_base_p;
+       u32                     sdma_base_p;
+       u32                     sdma_intr_base_p;
+       u32                     brg_base_p;
+
+       /* Virtual addresses of various blocks of registers (from platform) */
+       u32                     mpsc_base;
+       u32                     mpsc_routing_base;
+       u32                     sdma_base;
+       u32                     sdma_intr_base;
+       u32                     brg_base;
+
+       /* Descriptor ring and buffer allocations */
+       void                    *desc_region;   /* Region for desc rings */
+       dma_addr_t              desc_region_p;
+       u32                     desc_region_size;
+
+       void                    *buf_region;    /* kmalloc region for bufs */
+       u32                     buf_region_size;
+
+       mpsc_rx_desc_t          *rxr;           /* Rx descriptor ring */
+       mpsc_rx_desc_t          *rxr_p;         /* Phys addr of rxr */
+       u32                     rxr_posn;       /* First desc w/ Rx data */
+       u8                      *rxb;           /* Rx Ring I/O buf */
+       dma_addr_t              rxb_p;          /* Phys addr of rxb */
+
+       mpsc_tx_desc_t          *txr;           /* Tx descriptor ring */
+       mpsc_tx_desc_t          *txr_p;         /* Phys addr of txr */
+       u32                     txr_posn;       /* First unused desc */
+       u8                      *txb;           /* Tx Ring I/O buf */
+       dma_addr_t              txb_p;          /* Phys addr of txb */
+
+       /* Mirrored values of regs we can't read (if 'mirror_regs' set) */
+       u32                     MPSC_CHR_1_m;
+       u32                     MPSC_CHR_2_m;
+       u32                     MPSC_CHR_10_m;
+       u32                     MPSC_MPCR_m;
+       u32                     MPSC_MRR_m;
+       u32                     MPSC_RCRR_m;
+       u32                     MPSC_TCRR_m;
+       u32                     SDMA_INTR_MASK_m;
+       u32                     BRG_BCR_m;
+} mpsc_port_info_t;
+
+/*
+ * Some MPSC ctlrs have an erratum where they aren't supposed to access
+ * cache coherent memory regions.  From practical experience, the erratum
+ * is not triggered as long as there isn't a snoop hit.  Therefore, if
+ * the MPSC in used has this erratum and coherency is enabled on the platform,
+ * we must manually manage the cache for ring descriptors and the I/O buffers.
+ */
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+#define        MPSC_CACHE_FLUSH(pi, s, e) {                    \
+       if (pi->cache_mgmt) {                           \
+               /* 64x60 erratum: can't use dcbst/clean_dcache_range() */ \
+               flush_dcache_range((ulong)s, (ulong)e); \
+               mb();                                   \
+       }                                               \
+}
+
+#define        MPSC_CACHE_INVALIDATE(pi, s, e) {                       \
+       if (pi->cache_mgmt) {                                   \
+               invalidate_dcache_range((ulong)s, (ulong)e);    \
+               mb();                                           \
+       }                                                       \
+}
+
+#define        MPSC_CACHE_FLUSH_INVALIDATE(pi, s, e) {         \
+       if (pi->cache_mgmt) {                           \
+               flush_dcache_range((ulong)s, (ulong)e); \
+               mb();                                   \
+       }                                               \
+}
+#else
+#define        MPSC_CACHE_FLUSH(pi, s, e)
+#define        MPSC_CACHE_INVALIDATE(pi, s, e)
+#define        MPSC_CACHE_FLUSH_INVALIDATE(pi, s, e)
+#endif
+
+/*
+ * 'MASK_INSERT' takes the low-order 'n' bits of 'i', shifts it 'b' bits to
+ * the left, and inserts it into the target 't'.  The corresponding bits in
+ * 't' will have been cleared before the bits in 'i' are inserted.
+ */
+#ifdef CONFIG_PPC32
+#define MASK_INSERT(t, i, n, b) ({                             \
+       u32     rval = (t);                                     \
+        __asm__ __volatile__(                                  \
+               "rlwimi %0,%2,%4,32-(%3+%4),31-%4\n"            \
+               : "=r" (rval)                                   \
+               : "0" (rval), "r" (i), "i" (n), "i" (b));       \
+       rval;                                                   \
+})
+#else
+/* These macros are really just examples.  Feel free to change them --MAG */
+#define GEN_MASK(n, b)                 \
+({                                     \
+       u32     m, sl, sr;              \
+       sl = 32 - (n);                  \
+       sr = sl - (b);                  \
+       m = (0xffffffff << sl) >> sr;   \
+})
+
+#define MASK_INSERT(t, i, n, b)                \
+({                                     \
+       u32     m, rval = (t);          \
+       m = GEN_MASK((n), (b));         \
+       rval &= ~m;                     \
+       rval |= (((i) << (b)) & m);     \
+})
+#endif
+
+/* I/O macros for regs that you can read */
+#define        MPSC_READ(pi, unit, offset)     readl((pi)->unit##_base + (offset))
+#define        MPSC_WRITE(pi, unit, offset, v) writel(v, (pi)->unit##_base + (offset))
+#define        MPSC_MOD_FIELD(pi, unit, offset, num_bits, shift, val)  \
+{                                                              \
+       u32     v;                                              \
+       v = readl((pi)->unit##_base + (offset));                \
+       writel(MASK_INSERT(v,val,num_bits,shift), (pi)->unit##_base+(offset));\
+}
+
+#define        MPSC_READ_M(pi, unit, offset)                   \
+({                                                     \
+       u32     v;                                      \
+       if ((pi)->mirror_regs) v = (pi)->offset##_m;    \
+       else v = readl((pi)->unit##_base + (offset));   \
+       v;                                              \
+})
+
+#define        MPSC_WRITE_M(pi, unit, offset, v)               \
+({                                                     \
+       if ((pi)->mirror_regs) (pi)->offset##_m = v;    \
+       writel(v, (pi)->unit##_base + (offset));        \
+})
+
+#define        MPSC_MOD_FIELD_M(pi, unit, offset, num_bits, shift, val)        \
+({                                                                     \
+       u32     v;                                                      \
+       if ((pi)->mirror_regs) v = (pi)->offset##_m;                    \
+       else v = readl((pi)->unit##_base + (offset));                   \
+       v = MASK_INSERT(v, val, num_bits, shift);                       \
+       if ((pi)->mirror_regs) (pi)->offset##_m = v;                    \
+       writel(v, (pi)->unit##_base + (offset));                        \
+})
+
+#if !defined(MIN)
+#define        MIN(a, b)       (((a) < (b)) ? (a) : (b))
+#endif
+
+/* Hooks to platform-specific code */
+int mpsc_platform_register_driver(void);
+void mpsc_platform_unregister_driver(void);
+
+/* Hooks back in to mpsc common to be called by platform-specific code */
+mpsc_port_info_t *mpsc_device_probe(int index);
+mpsc_port_info_t *mpsc_device_remove(int index);
+
+#endif /* __MPSC_H__ */
diff --git a/drivers/serial/mpsc/mpsc_defs.h b/drivers/serial/mpsc/mpsc_defs.h
new file mode 100644 (file)
index 0000000..7bf095a
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * drivers/serial/mpsc/mpsc_defs.h
+ * 
+ * Register definitions for the Marvell Multi-Protocol Serial Controller (MPSC),
+ * Serial DMA Controller (SDMA), and Baud Rate Generator (BRG).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2004 (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        __MPSC_DEFS_H__
+#define        __MPSC_DEFS_H__
+
+#define        MPSC_NUM_CTLRS          2
+
+
+/*
+ *****************************************************************************
+ *
+ *     Multi-Protocol Serial Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+/* Main Configuratino Register Offsets */
+#define        MPSC_MMCRL                      0x0000
+#define        MPSC_MMCRH                      0x0004
+#define        MPSC_MPCR                       0x0008
+#define        MPSC_CHR_1                      0x000c
+#define        MPSC_CHR_2                      0x0010
+#define        MPSC_CHR_3                      0x0014
+#define        MPSC_CHR_4                      0x0018
+#define        MPSC_CHR_5                      0x001c
+#define        MPSC_CHR_6                      0x0020
+#define        MPSC_CHR_7                      0x0024
+#define        MPSC_CHR_8                      0x0028
+#define        MPSC_CHR_9                      0x002c
+#define        MPSC_CHR_10                     0x0030
+#define        MPSC_CHR_11                     0x0034
+#define        MPSC_REG_BLOCK_SIZE             0x0038
+
+
+#define        MPSC_MPCR_CL_5                  0
+#define        MPSC_MPCR_CL_6                  1
+#define        MPSC_MPCR_CL_7                  2
+#define        MPSC_MPCR_CL_8                  3
+#define        MPSC_MPCR_SBL_1                 0
+#define        MPSC_MPCR_SBL_2                 3
+
+#define        MPSC_CHR_2_TEV                  (1<<1)
+#define        MPSC_CHR_2_TA                   (1<<7)
+#define        MPSC_CHR_2_TTCS                 (1<<9)
+#define        MPSC_CHR_2_REV                  (1<<17)
+#define        MPSC_CHR_2_RA                   (1<<23)
+#define        MPSC_CHR_2_CRD                  (1<<25)
+#define        MPSC_CHR_2_EH                   (1<<31)
+#define        MPSC_CHR_2_PAR_ODD              0
+#define        MPSC_CHR_2_PAR_SPACE            1
+#define        MPSC_CHR_2_PAR_EVEN             2
+#define        MPSC_CHR_2_PAR_MARK             3
+
+/* MPSC Signal Routing */
+#define        MPSC_MRR                        0x0000
+#define        MPSC_RCRR                       0x0004
+#define        MPSC_TCRR                       0x0008
+#define        MPSC_ROUTING_REG_BLOCK_SIZE     0x000c
+
+/*
+ *****************************************************************************
+ *
+ *     Serial DMA Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        SDMA_SDC                        0x0000
+#define        SDMA_SDCM                       0x0008
+#define        SDMA_RX_DESC                    0x0800
+#define        SDMA_RX_BUF_PTR                 0x0808
+#define        SDMA_SCRDP                      0x0810
+#define        SDMA_TX_DESC                    0x0c00
+#define        SDMA_SCTDP                      0x0c10
+#define        SDMA_SFTDP                      0x0c14
+#define        SDMA_REG_BLOCK_SIZE             0x0c18
+
+#define        SDMA_DESC_CMDSTAT_PE            (1<<0)
+#define        SDMA_DESC_CMDSTAT_CDL           (1<<1)
+#define        SDMA_DESC_CMDSTAT_FR            (1<<3)
+#define        SDMA_DESC_CMDSTAT_OR            (1<<6)
+#define        SDMA_DESC_CMDSTAT_BR            (1<<9)
+#define        SDMA_DESC_CMDSTAT_MI            (1<<10)
+#define        SDMA_DESC_CMDSTAT_A             (1<<11)
+#define        SDMA_DESC_CMDSTAT_AM            (1<<12)
+#define        SDMA_DESC_CMDSTAT_CT            (1<<13)
+#define        SDMA_DESC_CMDSTAT_C             (1<<14)
+#define        SDMA_DESC_CMDSTAT_ES            (1<<15)
+#define        SDMA_DESC_CMDSTAT_L             (1<<16)
+#define        SDMA_DESC_CMDSTAT_F             (1<<17)
+#define        SDMA_DESC_CMDSTAT_P             (1<<18)
+#define        SDMA_DESC_CMDSTAT_EI            (1<<23)
+#define        SDMA_DESC_CMDSTAT_O             (1<<31)
+
+#define SDMA_DESC_DFLT                 (SDMA_DESC_CMDSTAT_O |  \
+                                       SDMA_DESC_CMDSTAT_EI)
+
+#define        SDMA_SDC_RFT                    (1<<0)
+#define        SDMA_SDC_SFM                    (1<<1)
+#define        SDMA_SDC_BLMR                   (1<<6)
+#define        SDMA_SDC_BLMT                   (1<<7)
+#define        SDMA_SDC_POVR                   (1<<8)
+#define        SDMA_SDC_RIFB                   (1<<9)
+
+#define        SDMA_SDCM_ERD                   (1<<7)
+#define        SDMA_SDCM_AR                    (1<<15)
+#define        SDMA_SDCM_STD                   (1<<16)
+#define        SDMA_SDCM_TXD                   (1<<23)
+#define        SDMA_SDCM_AT                    (1<<31)
+
+#define        SDMA_0_CAUSE_RXBUF              (1<<0)
+#define        SDMA_0_CAUSE_RXERR              (1<<1)
+#define        SDMA_0_CAUSE_TXBUF              (1<<2)
+#define        SDMA_0_CAUSE_TXEND              (1<<3)
+#define        SDMA_1_CAUSE_RXBUF              (1<<8)
+#define        SDMA_1_CAUSE_RXERR              (1<<9)
+#define        SDMA_1_CAUSE_TXBUF              (1<<10)
+#define        SDMA_1_CAUSE_TXEND              (1<<11)
+
+#define        SDMA_CAUSE_RX_MASK      (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR | \
+       SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR)
+#define        SDMA_CAUSE_TX_MASK      (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND | \
+       SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND)
+
+/* SDMA Interrupt registers */
+#define        SDMA_INTR_CAUSE                 0x0000
+#define        SDMA_INTR_MASK                  0x0080
+#define        SDMA_INTR_REG_BLOCK_SIZE        0x0084
+
+/*
+ *****************************************************************************
+ *
+ *     Baud Rate Generator Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        BRG_BCR                         0x0000
+#define        BRG_BTR                         0x0004
+#define        BRG_REG_BLOCK_SIZE              0x0008
+
+#endif /*__MPSC_DEFS_H__ */
diff --git a/drivers/serial/mpsc/mpsc_ppc32.c b/drivers/serial/mpsc/mpsc_ppc32.c
new file mode 100644 (file)
index 0000000..53b1dd4
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * drivers/serial/mpsc/mpsc_ppc32.c
+ *
+ * Middle layer that sucks data from the ppc32 OCP--that is, chip &
+ * platform-specific data--and puts it into the mpsc_port_info_t structure
+ * for the mpsc driver to use.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2004 (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 "mpsc.h"
+#include <asm/ocp.h>
+#include <asm/mv64x60.h>
+
+static void mpsc_ocp_remove(struct ocp_device *ocpdev);
+
+static int
+mpsc_ocp_probe(struct ocp_device *ocpdev)
+{
+       mpsc_port_info_t        *pi;
+       mv64x60_ocp_mpsc_data_t *dp;
+       u32                     base;
+       int                     rc = -ENODEV;
+
+       if ((pi = mpsc_device_probe(ocpdev->def->index)) != NULL) {
+               dp = (mv64x60_ocp_mpsc_data_t *)ocpdev->def->additions;
+
+               pi->mpsc_base_p = ocpdev->def->paddr;
+
+               if (ocpdev->def->index == 0) {
+                       base = pi->mpsc_base_p - MV64x60_MPSC_0_OFFSET;
+                       pi->sdma_base_p = base + MV64x60_SDMA_0_OFFSET;
+                       pi->brg_base_p = base + MV64x60_BRG_0_OFFSET;
+               }
+               else { /* Must be 1 */
+                       base = pi->mpsc_base_p - MV64x60_MPSC_1_OFFSET;
+                       pi->sdma_base_p = base + MV64x60_SDMA_1_OFFSET;
+                       pi->brg_base_p = base + MV64x60_BRG_1_OFFSET;
+               }
+
+               pi->mpsc_routing_base_p = base + MV64x60_MPSC_ROUTING_OFFSET;
+               pi->sdma_intr_base_p = base + MV64x60_SDMA_INTR_OFFSET;
+
+               pi->port.irq = dp->sdma_irq;
+               pi->port.uartclk = dp->brg_clk_freq;
+
+               pi->mirror_regs = dp->mirror_regs;
+               pi->cache_mgmt = dp->cache_mgmt;
+               pi->brg_can_tune = dp->brg_can_tune;
+               pi->brg_clk_src = dp->brg_clk_src;
+               pi->mpsc_max_idle = dp->max_idle;
+               pi->default_baud = dp->default_baud;
+               pi->default_bits = dp->default_bits;
+               pi->default_parity = dp->default_parity;
+               pi->default_flow = dp->default_flow;
+
+               /* Initial values of mirrored regs */
+               pi->MPSC_CHR_1_m = dp->chr_1_val;
+               pi->MPSC_CHR_2_m = dp->chr_2_val;
+               pi->MPSC_CHR_10_m = dp->chr_10_val;
+               pi->MPSC_MPCR_m = dp->mpcr_val;
+               pi->MPSC_MRR_m = dp->mrr_val;
+               pi->MPSC_RCRR_m = dp->rcrr_val;
+               pi->MPSC_TCRR_m = dp->tcrr_val;
+               pi->SDMA_INTR_MASK_m = dp->intr_mask_val;
+               pi->BRG_BCR_m = dp->bcr_val;
+
+               pi->port.iotype = UPIO_MEM;
+
+               rc = 0;
+       }
+
+       return rc;
+}
+
+static void
+mpsc_ocp_remove(struct ocp_device *ocpdev)
+{
+       (void)mpsc_device_remove(ocpdev->def->index);
+       return;
+}
+
+static struct ocp_device_id mpsc_ocp_ids[] = {
+       {.vendor = OCP_VENDOR_MARVELL, .function = OCP_FUNC_MPSC},
+       {.vendor = OCP_VENDOR_INVALID}
+};
+
+static struct ocp_driver mpsc_ocp_driver = {
+       .name = "mpsc",
+       .id_table = mpsc_ocp_ids,
+       .probe = mpsc_ocp_probe,
+       .remove = mpsc_ocp_remove,
+};
+
+int
+mpsc_platform_register_driver(void)
+{
+       return ocp_register_driver(&mpsc_ocp_driver);
+}
+
+void
+mpsc_platform_unregister_driver(void)
+{
+       ocp_unregister_driver(&mpsc_ocp_driver);
+       return;
+}
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h
new file mode 100644 (file)
index 0000000..5e3445d
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                    University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in the 
+ * jffs2 directory.
+ *
+ * $Id: compr.h,v 1.5 2004/06/23 16:34:39 havasi Exp $
+ *
+ */
+
+#ifndef __JFFS2_COMPR_H__
+#define __JFFS2_COMPR_H__
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/jffs2.h>
+#include <linux/jffs2_fs_i.h>
+#include <linux/jffs2_fs_sb.h>
+#include "nodelist.h"
+
+#define JFFS2_RUBINMIPS_PRIORITY 10
+#define JFFS2_DYNRUBIN_PRIORITY  20
+#define JFFS2_LZARI_PRIORITY     30
+#define JFFS2_LZO_PRIORITY       40
+#define JFFS2_RTIME_PRIORITY     50
+#define JFFS2_ZLIB_PRIORITY      60
+
+#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
+#define JFFS2_DYNRUBIN_DISABLED  /*        for decompression */
+
+#define JFFS2_COMPR_MODE_NONE       0
+#define JFFS2_COMPR_MODE_PRIORITY   1
+#define JFFS2_COMPR_MODE_SIZE       2
+
+void jffs2_set_compression_mode(int mode);
+int jffs2_get_compression_mode(void);
+
+struct jffs2_compressor {
+        struct list_head list;
+        int priority;              /* used by prirority comr. mode */
+        char *name;
+        char compr;                /* JFFS2_COMPR_XXX */
+        int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
+                        uint32_t *srclen, uint32_t *destlen, void *model);
+        int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
+                        uint32_t cdatalen, uint32_t datalen, void *model);
+        int usecount;
+        int disabled;              /* if seted the compressor won't compress */
+        unsigned char *compr_buf;  /* used by size compr. mode */
+        uint32_t compr_buf_size;   /* used by size compr. mode */
+        uint32_t stat_compr_orig_size;
+        uint32_t stat_compr_new_size;
+        uint32_t stat_compr_blocks;
+        uint32_t stat_decompr_blocks;
+};
+
+int jffs2_register_compressor(struct jffs2_compressor *comp);
+int jffs2_unregister_compressor(struct jffs2_compressor *comp);
+
+int jffs2_compressors_init(void);
+int jffs2_compressors_exit(void);
+
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                             unsigned char *data_in, unsigned char **cpage_out,
+                             uint32_t *datalen, uint32_t *cdatalen);
+
+int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                     uint16_t comprtype, unsigned char *cdata_in,
+                     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
+
+#ifdef CONFIG_JFFS2_PROC
+int jffs2_enable_compressor_name(const char *name);
+int jffs2_disable_compressor_name(const char *name);
+int jffs2_set_compression_mode_name(const char *mode_name);
+char *jffs2_get_compression_mode_name(void);
+int jffs2_set_compressor_priority(const char *mode_name, int priority);
+char *jffs2_list_compressors(void);
+char *jffs2_stats(void);
+#endif
+
+/* Compressor modules */
+/* These functions will be called by jffs2_compressors_init/exit */
+
+#ifdef CONFIG_JFFS2_RUBIN
+int jffs2_rubinmips_init(void);
+void jffs2_rubinmips_exit(void);
+int jffs2_dynrubin_init(void);
+void jffs2_dynrubin_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+int jffs2_rtime_init(void);
+void jffs2_rtime_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+int jffs2_lzari_init(void);
+void jffs2_lzari_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_LZO
+int jffs2_lzo_init(void);
+void jffs2_lzo_exit(void);
+#endif
+
+/* Prototypes from proc.c */
+int jffs2_proc_init(void);
+int jffs2_proc_exit(void);
+
+#endif /* __JFFS2_COMPR_H__ */
diff --git a/fs/jffs2/proc.c b/fs/jffs2/proc.c
new file mode 100644 (file)
index 0000000..36c0a0e
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                    University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: proc.c,v 1.3 2004/06/24 09:51:38 havasi Exp $
+ *
+ * Files in /proc/fs/jffs2 directory:
+ *   compr_list
+ *         read:  shows the list of the loaded compressors 
+ *                (name, priority, enadbled/disabled)
+ *         write: compressors can be enabled/disabled and
+ *                the priority of them can be changed,
+ *                required formats:
+ *                    enable COMPRESSOR_NAME
+ *                    disble COMPRESSOR_NAME
+ *                    priority NEW_PRIORITY COMPRESSOR_NAME
+ *   compr_mode
+ *         read:  shows the name of the actual compression mode
+ *         write: sets the actual comperession mode
+ *   compr_stat
+ *         read:  shows compression statistics
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/jffs.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include "compr.h"
+
+extern struct proc_dir_entry *jffs_proc_root;
+
+/* Structure for top-level entry in '/proc/fs' directory */
+static struct proc_dir_entry *jffs2_proc_root;
+
+/* Structure for files in /proc/fs/jffs2 directory */
+static struct proc_dir_entry *jffs2_proc_compr_stat;
+static struct proc_dir_entry *jffs2_proc_compr_mode;
+
+/* Read the JFFS2 'compr_stat' file */
+
+static int jffs2_proc_stat_read (char *page, char **start, off_t off,
+               int count, int *eof, void *data)
+{
+       int len = 0,i;
+        char *stat = jffs2_stats();
+        
+        if (strlen(stat)<off) {
+               *eof = 1;
+                kfree(stat);
+               return len;
+        }        
+        for (i=off;((stat[i]!=0)&&(len<count));i++,len++) {
+                page[len]=stat[i];
+        }
+        if (off+len>=strlen(stat)) *eof = 1;
+        else *eof = 0;
+        kfree(stat);
+       return len;
+}
+
+
+/* Read the JFFS2 'compr_mode' file */
+
+static int jffs2_proc_mode_read (char *page, char **start, off_t off,
+               int count, int *eof, void *data)
+{
+       int len = 0;
+        if (strlen(jffs2_get_compression_mode_name())+1>count) {
+                /* it should not happen */
+               *eof = 1;
+                return 0;
+        }
+       len += sprintf(page, "%s\n",jffs2_get_compression_mode_name());
+       *eof = 1;
+       return len;
+}
+
+/* Write the JFFS2 'compr_mode' file
+ *   sets the actual compression mode
+ */
+
+static int jffs2_proc_mode_write(struct file *file, const char *buffer,
+                           unsigned long count, void *data)
+{
+        char *compr_name;
+
+        /* collect the name of the compression mode and set it */
+        compr_name = kmalloc(count+1,GFP_KERNEL);
+        if (sscanf(buffer,"%s",compr_name)>0) {
+                if (jffs2_set_compression_mode_name(compr_name)) {
+                        printk(KERN_WARNING "JFFS2: error switching compression mode. Invalid parameter (%s)?\n",compr_name);
+                }
+        }
+        else {
+                printk(KERN_WARNING "JFFS2: error: parameter missing\n");
+        }
+        kfree(compr_name);
+        return count;
+}
+
+/* Read the JFFS2 'compr_list' file */
+
+static int jffs2_proc_list_read (char *page, char **start, off_t off,
+               int count, int *eof, void *data)
+{
+       int len = 0;
+        char *list = jffs2_list_compressors();
+        if (strlen(list)+1>count) {
+                /* it should not happen */
+               *eof = 1;
+                kfree(list);
+                return 0;
+        }
+       len += sprintf(page,"%s",list);
+       *eof = 1;
+        kfree(list);
+       return len;
+}
+
+/* Write the JFFS2 'compr_list' file 
+ *   enable/disable a compressor or set the priority of it
+ */
+
+static int jffs2_proc_list_write(struct file *file, const char *buffer,
+                           unsigned long count, void *data)
+{
+        int prior;
+        char *compr_name,*compr_cmd;
+
+        compr_name = kmalloc(count+1,GFP_KERNEL);
+        compr_cmd = kmalloc(count+1,GFP_KERNEL);
+        if (!compr_name) {
+                printk(KERN_WARNING "JFFS2: unable to allocate memory\n");
+                goto list_write_end;
+        }
+        compr_name[0] = 0;
+
+        if (sscanf(buffer,"priority %d %s",&prior,compr_name)>1) {
+                jffs2_set_compressor_priority(compr_name, prior);
+                goto list_write_end;
+        }
+        if (sscanf(buffer,"enable %s",compr_name)>0) {
+                jffs2_enable_compressor_name(compr_name);
+                goto list_write_end;
+        }
+        if (sscanf(buffer,"disable %s",compr_name)>0) {
+                jffs2_disable_compressor_name(compr_name);
+                goto list_write_end;
+        }
+        printk(KERN_WARNING "JFFS2: usage of /proc/fs/jffs2/compr_list:\n"
+               "  echo \"enable COMPRESSOR_NAME\"  >/proc/fs/jffs2/compr_list\n"
+               "  echo \"disable COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n"
+               "  echo \"priority NEW_PRIORITY COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n");
+list_write_end:
+        kfree(compr_cmd);
+        kfree(compr_name);
+       return count;
+}
+
+/* Register a JFFS2 proc directory */
+
+int jffs2_proc_init(void)
+{
+       jffs2_proc_root = proc_mkdir("jffs2", proc_root_fs);
+
+       /* create entry for 'compr_stat' file */
+       if ((jffs2_proc_compr_stat = create_proc_entry ("compr_stat", 0, jffs2_proc_root))) {
+               jffs2_proc_compr_stat->read_proc = jffs2_proc_stat_read;
+       }
+       else {
+               return -ENOMEM;
+       }
+       /* create entry for 'compr_mode' file */
+       if ((jffs2_proc_compr_mode = create_proc_entry ("compr_mode", 0, jffs2_proc_root))) {
+               jffs2_proc_compr_mode->read_proc  = jffs2_proc_mode_read;
+               jffs2_proc_compr_mode->write_proc = jffs2_proc_mode_write;
+       }
+       else {
+               return -ENOMEM;
+       }
+       /* create entry for 'compr_list' file */
+       if ((jffs2_proc_compr_mode = create_proc_entry ("compr_list", 0, jffs2_proc_root))) {
+               jffs2_proc_compr_mode->read_proc  = jffs2_proc_list_read;
+               jffs2_proc_compr_mode->write_proc = jffs2_proc_list_write;
+       }
+       else {
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+
+/* Unregister a JFFS2 proc directory */
+
+int jffs2_proc_exit(void)
+{
+#if LINUX_VERSION_CODE < 0x020300
+       remove_proc_entry ("compr_stat", &jffs2_proc_root);
+       remove_proc_entry ("compr_mode", &jffs2_proc_root);
+       remove_proc_entry ("compr_list", &jffs2_proc_root);
+       remove_proc_entry ("jffs2", &proc_root_fs);
+#else
+       remove_proc_entry ("compr_stat", jffs2_proc_root);
+       remove_proc_entry ("compr_mode", jffs2_proc_root);
+       remove_proc_entry ("compr_list", jffs2_proc_root);
+       remove_proc_entry ("jffs2", proc_root_fs);
+#endif
+        return 0;
+}
diff --git a/include/asm-ppc/mv64x60.h b/include/asm-ppc/mv64x60.h
new file mode 100644 (file)
index 0000000..0aa5482
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * include/asm-ppc/mv64x60.h
+ * 
+ * Prototypes, etc. for the Marvell/Galileo MV64x60 host bridge routines.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2001-2002 (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 __ASMPPC_MV64x60_H
+#define __ASMPPC_MV64x60_H
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/config.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/mv64x60_defs.h>
+
+extern u8      mv64x60_pci_exclude_bridge;
+
+extern spinlock_t mv64x60_lock;
+extern spinlock_t mv64x60_rmw_lock;
+
+#ifndef        TRUE
+#define        TRUE    1
+#endif
+
+#ifndef        FALSE
+#define        FALSE   0
+#endif
+
+/* 32-bit Window table entry defines */
+#define        MV64x60_CPU2MEM_0_WIN                   0
+#define        MV64x60_CPU2MEM_1_WIN                   1
+#define        MV64x60_CPU2MEM_2_WIN                   2
+#define        MV64x60_CPU2MEM_3_WIN                   3
+#define        MV64x60_CPU2DEV_0_WIN                   4
+#define        MV64x60_CPU2DEV_1_WIN                   5
+#define        MV64x60_CPU2DEV_2_WIN                   6
+#define        MV64x60_CPU2DEV_3_WIN                   7
+#define        MV64x60_CPU2BOOT_WIN                    8
+#define        MV64x60_CPU2PCI0_IO_WIN                 9
+#define        MV64x60_CPU2PCI0_MEM_0_WIN              10
+#define        MV64x60_CPU2PCI0_MEM_1_WIN              11
+#define        MV64x60_CPU2PCI0_MEM_2_WIN              12
+#define        MV64x60_CPU2PCI0_MEM_3_WIN              13
+#define        MV64x60_CPU2PCI1_IO_WIN                 14
+#define        MV64x60_CPU2PCI1_MEM_0_WIN              15
+#define        MV64x60_CPU2PCI1_MEM_1_WIN              16
+#define        MV64x60_CPU2PCI1_MEM_2_WIN              17
+#define        MV64x60_CPU2PCI1_MEM_3_WIN              18
+#define        MV64x60_CPU2SRAM_WIN                    19
+#define        MV64x60_CPU2PCI0_IO_REMAP_WIN           20
+#define        MV64x60_CPU2PCI1_IO_REMAP_WIN           21
+#define        MV64x60_CPU_PROT_0_WIN                  22
+#define        MV64x60_CPU_PROT_1_WIN                  23
+#define        MV64x60_CPU_PROT_2_WIN                  24
+#define        MV64x60_CPU_PROT_3_WIN                  25
+#define        MV64x60_CPU_SNOOP_0_WIN                 26
+#define        MV64x60_CPU_SNOOP_1_WIN                 27
+#define        MV64x60_CPU_SNOOP_2_WIN                 28
+#define        MV64x60_CPU_SNOOP_3_WIN                 29
+#define        MV64x60_PCI02MEM_REMAP_0_WIN            30
+#define        MV64x60_PCI02MEM_REMAP_1_WIN            31
+#define        MV64x60_PCI02MEM_REMAP_2_WIN            32
+#define        MV64x60_PCI02MEM_REMAP_3_WIN            33
+#define        MV64x60_PCI12MEM_REMAP_0_WIN            34
+#define        MV64x60_PCI12MEM_REMAP_1_WIN            35
+#define        MV64x60_PCI12MEM_REMAP_2_WIN            36
+#define        MV64x60_PCI12MEM_REMAP_3_WIN            37
+
+#define        MV64x60_32BIT_WIN_COUNT                 38
+
+/* 64-bit Window table entry defines */
+#define        MV64x60_CPU2PCI0_MEM_0_REMAP_WIN        0
+#define        MV64x60_CPU2PCI0_MEM_1_REMAP_WIN        1
+#define        MV64x60_CPU2PCI0_MEM_2_REMAP_WIN        2
+#define        MV64x60_CPU2PCI0_MEM_3_REMAP_WIN        3
+#define        MV64x60_CPU2PCI1_MEM_0_REMAP_WIN        4
+#define        MV64x60_CPU2PCI1_MEM_1_REMAP_WIN        5
+#define        MV64x60_CPU2PCI1_MEM_2_REMAP_WIN        6
+#define        MV64x60_CPU2PCI1_MEM_3_REMAP_WIN        7
+#define        MV64x60_PCI02MEM_ACC_CNTL_0_WIN         8
+#define        MV64x60_PCI02MEM_ACC_CNTL_1_WIN         9
+#define        MV64x60_PCI02MEM_ACC_CNTL_2_WIN         10
+#define        MV64x60_PCI02MEM_ACC_CNTL_3_WIN         11
+#define        MV64x60_PCI12MEM_ACC_CNTL_0_WIN         12
+#define        MV64x60_PCI12MEM_ACC_CNTL_1_WIN         13
+#define        MV64x60_PCI12MEM_ACC_CNTL_2_WIN         14
+#define        MV64x60_PCI12MEM_ACC_CNTL_3_WIN         15
+#define        MV64x60_PCI02MEM_SNOOP_0_WIN            16
+#define        MV64x60_PCI02MEM_SNOOP_1_WIN            17
+#define        MV64x60_PCI02MEM_SNOOP_2_WIN            18
+#define        MV64x60_PCI02MEM_SNOOP_3_WIN            19
+#define        MV64x60_PCI12MEM_SNOOP_0_WIN            20
+#define        MV64x60_PCI12MEM_SNOOP_1_WIN            21
+#define        MV64x60_PCI12MEM_SNOOP_2_WIN            22
+#define        MV64x60_PCI12MEM_SNOOP_3_WIN            23
+
+#define        MV64x60_64BIT_WIN_COUNT                 24
+
+
+/*
+ * Define a structure that's used to pass in config information to the
+ * core routines.
+ */
+typedef struct {
+       u32     cpu_base;
+       u32     pci_base_hi;
+       u32     pci_base_lo;
+       u32     size;
+       u32     swap;
+} mv64x60_pci_window_t;
+
+typedef        struct {
+       u8      enable_bus;     /* allow access to this PCI bus? */
+       u8      enumerate_bus;  /* enumerate devices on this bus? */
+
+       mv64x60_pci_window_t    pci_io;
+       mv64x60_pci_window_t    pci_mem[3];
+
+       u32     acc_cntl_options[MV64x60_CPU2MEM_WINDOWS];
+       u32     snoop_options[MV64x60_CPU2MEM_WINDOWS];
+       u16     pci_cmd_bits;
+       u16     latency_timer;
+} mv64x60_pci_info_t;
+
+typedef struct {
+       u32     phys_reg_base;
+
+       u32     window_preserve_mask_32;
+       u32     window_preserve_mask_64;
+
+       u32     base_irq;       /* Starting irq # for this intr ctlr */
+       int     ((*map_irq)(struct pci_dev *, unsigned char, unsigned char));
+
+       u32     cpu_prot_options[MV64x60_CPU2MEM_WINDOWS];
+       u32     cpu_snoop_options[MV64x60_CPU2MEM_WINDOWS];
+
+       mv64x60_pci_info_t      pci_0;
+       mv64x60_pci_info_t      pci_1;
+} mv64x60_setup_info_t;
+
+/*
+ * Define the 'handle' struct that will be passed between the 64x60 core
+ * code and the platform-specific code that will use it.  The handle
+ * will contain pointers to chip-specific routines & information.
+ */
+typedef struct {
+       u32     base_reg;
+       u32     size_reg;
+       u8      base_bits;
+       u8      size_bits;
+       u32     (*get_from_field)(u32 val, u32 num_bits);
+       u32     (*map_to_field)(u32 val, u32 num_bits);
+       u32     extra;
+} mv64x60_32bit_window_t;
+
+typedef struct {
+       u32     base_hi_reg;
+       u32     base_lo_reg;
+       u32     size_reg;
+       u8      base_lo_bits;
+       u8      size_bits;
+       u32     (*get_from_field)(u32 val, u32 num_bits);
+       u32     (*map_to_field)(u32 val, u32 num_bits);
+       u32     extra;
+} mv64x60_64bit_window_t;
+
+typedef        struct mv64x60_handle   mv64x60_handle_t;
+
+typedef struct {
+       u32     (*translate_size)(u32 base, u32 size, u32 num_bits);
+       u32     (*untranslate_size)(u32 base, u32 size, u32 num_bits);
+       void    (*set_pci2mem_window)(struct pci_controller *hose, u32 window,
+                       u32 base);
+       u32     (*is_enabled_32bit)(mv64x60_handle_t *bh, u32 window);
+       void    (*enable_window_32bit)(mv64x60_handle_t *bh, u32 window);
+       void    (*disable_window_32bit)(mv64x60_handle_t *bh, u32 window);
+       void    (*enable_window_64bit)(mv64x60_handle_t *bh, u32 window);
+       void    (*disable_window_64bit)(mv64x60_handle_t *bh, u32 window);
+       void    (*disable_all_windows)(mv64x60_handle_t *bh,
+                                      mv64x60_setup_info_t *si);
+       void    (*chip_specific_init)(mv64x60_handle_t *bh,
+                                     mv64x60_setup_info_t *si);
+
+       mv64x60_32bit_window_t  *window_tab_32bit;
+       mv64x60_64bit_window_t  *window_tab_64bit;
+} mv64x60_chip_info_t;
+
+struct mv64x60_handle {
+       u32     type;           /* type of bridge */
+       u32     v_base;         /* virtual base addr of bridge regs */
+       u32     p_base;         /* physical base addr of bridge regs */
+       u32     base_irq;       /* Base irq # for intrs on this intr cltr */
+
+       u32     io_base_a;      /* vaddr of pci 0's I/O space */
+       u32     io_base_b;      /* vaddr of pci 1's I/O space */
+
+       struct pci_controller   *hose_a;
+       struct pci_controller   *hose_b;
+
+       mv64x60_chip_info_t     *ci;    /* chip/bridge-specific info */
+};
+
+
+/* Define I/O routines for accessing registers on the 64x60 bridge. */
+extern inline void
+mv64x60_write(mv64x60_handle_t *bh, u32 offset, u32 val) {
+       out_le32((volatile u32 *)(bh->v_base + offset), val);
+}
+
+extern inline u32
+mv64x60_read(mv64x60_handle_t *bh, u32 offset) {
+       return in_le32((volatile u32 *)(bh->v_base + offset));
+}
+
+extern inline void
+mv64x60_modify(mv64x60_handle_t *bh, u32 offs, u32 data, u32 mask)
+{
+       uint32_t reg;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mv64x60_rmw_lock, flags);
+       reg = mv64x60_read(bh, offs) & (~mask); /* zero any bits we care about*/
+       reg |= data & mask; /* set bits from the data */
+       mv64x60_write(bh, offs, reg);
+       spin_unlock_irqrestore(&mv64x60_rmw_lock, flags);
+}
+
+#define        mv64x60_set_bits(bh, offs, bits) mv64x60_modify(bh, offs, ~0, bits) 
+#define        mv64x60_clr_bits(bh, offs, bits) mv64x60_modify(bh, offs, 0, bits)
+
+
+/* Externally visible function prototypes */
+int mv64x60_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si);
+u32 mv64x60_get_mem_size(u32 bridge_base, u32 chip_type);
+void mv64x60_get_32bit_window(mv64x60_handle_t *bh, u32 window,
+                                       u32 *base, u32 *size);
+void mv64x60_set_32bit_window(mv64x60_handle_t *bh, u32 window, u32 base,
+                                       u32 size, u32 other_bits);
+void mv64x60_get_64bit_window(mv64x60_handle_t *bh, u32 window, u32 *base_hi,
+                                       u32 *base_lo, u32 *size);
+void mv64x60_set_64bit_window(mv64x60_handle_t *bh, u32 window, u32 base_hi,
+                                       u32 base_lo, u32 size, u32 other_bits);
+
+
+void gt64260_init_irq(void);
+int gt64260_get_irq(struct pt_regs *regs);
+
+/*
+ * OCP Related Definitions
+ */
+typedef struct {
+       u8      mirror_regs;
+       u8      cache_mgmt;
+       u8      max_idle;
+       int     default_baud;
+       int     default_bits;
+       int     default_parity;
+       int     default_flow;
+       u32     chr_1_val;
+       u32     chr_2_val;
+       u32     chr_10_val;
+       u32     mpcr_val;
+       u32     mrr_val;
+       u32     rcrr_val;
+       u32     tcrr_val;
+       u32     intr_mask_val;
+       u32     bcr_val;
+       u32     sdma_irq;
+       u8      brg_can_tune;
+       u8      brg_clk_src;
+       u32     brg_clk_freq;
+} mv64x60_ocp_mpsc_data_t;
+
+#define MV64x60_OCP_SYSFS_MPSC_DATA()                                  \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, mirror_regs)    \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, cache_mgmt)     \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, max_idle)       \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, default_baud)   \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, default_bits)   \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%c\n", mpsc, default_parity) \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%c\n", mpsc, default_flow)   \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, chr_1_val)    \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, chr_2_val)    \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, chr_10_val)   \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, mpcr_val)     \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, mrr_val)      \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, rcrr_val)     \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, tcrr_val)     \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, intr_mask_val)        \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, bcr_val)      \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, sdma_irq)       \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, brg_can_tune)   \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, brg_clk_src)    \
+OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, brg_clk_freq)   \
+                                                                       \
+void                                                                   \
+mv64x60_ocp_show_mpsc(struct device *dev)                              \
+{                                                                      \
+       device_create_file(dev, &dev_attr_mpsc_mirror_regs);            \
+       device_create_file(dev, &dev_attr_mpsc_cache_mgmt);             \
+       device_create_file(dev, &dev_attr_mpsc_max_idle);               \
+       device_create_file(dev, &dev_attr_mpsc_default_baud);           \
+       device_create_file(dev, &dev_attr_mpsc_default_bits);           \
+       device_create_file(dev, &dev_attr_mpsc_default_parity);         \
+       device_create_file(dev, &dev_attr_mpsc_default_flow);           \
+       device_create_file(dev, &dev_attr_mpsc_chr_1_val);              \
+       device_create_file(dev, &dev_attr_mpsc_chr_2_val);              \
+       device_create_file(dev, &dev_attr_mpsc_chr_10_val);             \
+       device_create_file(dev, &dev_attr_mpsc_mpcr_val);               \
+       device_create_file(dev, &dev_attr_mpsc_mrr_val);                \
+       device_create_file(dev, &dev_attr_mpsc_rcrr_val);               \
+       device_create_file(dev, &dev_attr_mpsc_tcrr_val);               \
+       device_create_file(dev, &dev_attr_mpsc_intr_mask_val);          \
+       device_create_file(dev, &dev_attr_mpsc_bcr_val);                \
+       device_create_file(dev, &dev_attr_mpsc_sdma_irq);               \
+       device_create_file(dev, &dev_attr_mpsc_brg_can_tune);           \
+       device_create_file(dev, &dev_attr_mpsc_brg_clk_src);            \
+       device_create_file(dev, &dev_attr_mpsc_brg_clk_freq);           \
+}
+
+#endif /* __ASMPPC_MV64x60_H */
diff --git a/include/asm-ppc/mv64x60_defs.h b/include/asm-ppc/mv64x60_defs.h
new file mode 100644 (file)
index 0000000..6f7899d
--- /dev/null
@@ -0,0 +1,996 @@
+/*
+ * include/asm-ppc/gt64260_defs.h
+ * 
+ * Register definitions for the Marvell/Galileo GT64260, MV64360, etc.
+ * host bridges.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2001-2002 (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 __ASMPPC_MV64x60_DEFS_H
+#define __ASMPPC_MV64x60_DEFS_H
+
+/*
+ * Define the Marvell bridges that are supported
+ */
+#define        MV64x60_TYPE_INVALID            0
+#define        MV64x60_TYPE_GT64260A           1
+#define        MV64x60_TYPE_GT64260B           2
+#define        MV64x60_TYPE_MV64360            3
+#define        MV64x60_TYPE_MV64361            4
+#define        MV64x60_TYPE_MV64362            5
+#define        MV64x60_TYPE_MV64460            6
+
+
+/* Revisions of each supported chip */
+#define        GT64260_REV_A                   0x10
+#define        GT64260_REV_B                   0x20
+#define        MV64360                         0x01
+#define        MV64460                         0x01
+
+/* Minimum window size supported by 64260 is 1MB */
+#define GT64260_WINDOW_SIZE_MIN                0x00100000
+#define MV64360_WINDOW_SIZE_MIN                0x00010000
+
+/* IRQ's for embedded controllers */
+#define        MV64x60_IRQ_DEV                 1
+#define        MV64x60_IRQ_CPU_ERR             3
+#define        MV64x60_IRQ_TIMER_0_1           8
+#define        MV64x60_IRQ_TIMER_2_3           9
+#define        MV64x60_IRQ_TIMER_4_5           10
+#define        MV64x60_IRQ_TIMER_6_7           11
+#define        MV64x60_IRQ_ETH_0               32
+#define        MV64x60_IRQ_ETH_1               33
+#define        MV64x60_IRQ_ETH_2               34
+#define        MV64x60_IRQ_SDMA_0              36
+#define        MV64x60_IRQ_I2C                 37
+#define        MV64x60_IRQ_SDMA_1              38
+#define        MV64x60_IRQ_BRG                 39
+#define        MV64x60_IRQ_MPSC_0              40
+#define        MV64x60_IRQ_MPSC_1              42
+#define        MV64x60_IRQ_COMM                43
+
+#define        MV64360_IRQ_PCI0                12
+#define        MV64360_IRQ_SRAM_PAR_ERR        13
+#define        MV64360_IRQ_PCI1                16
+
+/* Offsets for register blocks */
+#define        MV64x60_MPSC_0_OFFSET           0x8000
+#define        MV64x60_MPSC_1_OFFSET           0x9000
+#define        MV64x60_MPSC_ROUTING_OFFSET     0xb400
+#define        MV64x60_SDMA_0_OFFSET           0x4000
+#define        MV64x60_SDMA_1_OFFSET           0x6000
+#define        MV64x60_SDMA_INTR_OFFSET        0xb800
+#define        MV64x60_BRG_0_OFFSET            0xb200
+#define        MV64x60_BRG_1_OFFSET            0xb208
+
+/*
+ *****************************************************************************
+ *
+ *     CPU Interface Registers
+ *
+ *****************************************************************************
+ */
+
+/* CPU physical address of 64260's registers */
+#define MV64x60_INTERNAL_SPACE_DECODE                  0x0068
+#define MV64x60_INTERNAL_SPACE_SIZE                    0x10000
+#define MV64x60_INTERNAL_SPACE_DEFAULT_ADDR            0x14000000
+
+#define        MV64360_CPU_BAR_ENABLE                          0x0278
+
+/* CPU Memory Controller Window Registers (4 windows) */
+#define        MV64x60_CPU2MEM_WINDOWS                         4
+
+#define        MV64x60_CPU2MEM_0_BASE                          0x0008
+#define        MV64x60_CPU2MEM_0_SIZE                          0x0010
+#define        MV64x60_CPU2MEM_1_BASE                          0x0208
+#define        MV64x60_CPU2MEM_1_SIZE                          0x0210
+#define        MV64x60_CPU2MEM_2_BASE                          0x0018
+#define        MV64x60_CPU2MEM_2_SIZE                          0x0020
+#define        MV64x60_CPU2MEM_3_BASE                          0x0218
+#define        MV64x60_CPU2MEM_3_SIZE                          0x0220
+
+/* CPU Device Controller Window Registers (4 windows) */
+#define        MV64x60_CPU2DEV_CS_WINDOWS                      4
+
+#define        MV64x60_CPU2DEV_0_BASE                          0x0028
+#define        MV64x60_CPU2DEV_0_SIZE                          0x0030
+#define        MV64x60_CPU2DEV_1_BASE                          0x0228
+#define        MV64x60_CPU2DEV_1_SIZE                          0x0230
+#define        MV64x60_CPU2DEV_2_BASE                          0x0248
+#define        MV64x60_CPU2DEV_2_SIZE                          0x0250
+#define        MV64x60_CPU2DEV_3_BASE                          0x0038
+#define        MV64x60_CPU2DEV_3_SIZE                          0x0040
+
+#define        MV64x60_CPU2BOOT_0_BASE                         0x0238
+#define        MV64x60_CPU2BOOT_0_SIZE                         0x0240
+
+/* CPU Windows to PCI space (2 PCI buses each w/ 1 I/O & 4 MEM windows) */
+#define        MV64x60_PCI_BUSES                               2
+#define        MV64x60_PCI_IO_WINDOWS_PER_BUS                  1
+#define        MV64x60_PCI_MEM_WINDOWS_PER_BUS                 4
+
+#define        MV64x60_CPU2PCI_SWAP_BYTE                       0x00000000
+#define        MV64x60_CPU2PCI_SWAP_NONE                       0x01000000
+#define        MV64x60_CPU2PCI_SWAP_BYTE_WORD                  0x02000000
+#define        MV64x60_CPU2PCI_SWAP_WORD                       0x03000000
+
+#define        MV64x60_CPU2PCI_MEM_REQ64                       (1<<27)
+
+#define        MV64x60_CPU2PCI0_IO_BASE                        0x0048
+#define        MV64x60_CPU2PCI0_IO_SIZE                        0x0050
+#define        MV64x60_CPU2PCI0_MEM_0_BASE                     0x0058
+#define        MV64x60_CPU2PCI0_MEM_0_SIZE                     0x0060
+#define        MV64x60_CPU2PCI0_MEM_1_BASE                     0x0080
+#define        MV64x60_CPU2PCI0_MEM_1_SIZE                     0x0088
+#define        MV64x60_CPU2PCI0_MEM_2_BASE                     0x0258
+#define        MV64x60_CPU2PCI0_MEM_2_SIZE                     0x0260
+#define        MV64x60_CPU2PCI0_MEM_3_BASE                     0x0280
+#define        MV64x60_CPU2PCI0_MEM_3_SIZE                     0x0288
+
+#define        MV64x60_CPU2PCI0_IO_REMAP                       0x00f0
+#define        MV64x60_CPU2PCI0_MEM_0_REMAP_LO                 0x00f8
+#define        MV64x60_CPU2PCI0_MEM_0_REMAP_HI                 0x0320
+#define        MV64x60_CPU2PCI0_MEM_1_REMAP_LO                 0x0100
+#define        MV64x60_CPU2PCI0_MEM_1_REMAP_HI                 0x0328
+#define        MV64x60_CPU2PCI0_MEM_2_REMAP_LO                 0x02f8
+#define        MV64x60_CPU2PCI0_MEM_2_REMAP_HI                 0x0330
+#define        MV64x60_CPU2PCI0_MEM_3_REMAP_LO                 0x0300
+#define        MV64x60_CPU2PCI0_MEM_3_REMAP_HI                 0x0338
+
+#define        MV64x60_CPU2PCI1_IO_BASE                        0x0090
+#define        MV64x60_CPU2PCI1_IO_SIZE                        0x0098
+#define        MV64x60_CPU2PCI1_MEM_0_BASE                     0x00a0
+#define        MV64x60_CPU2PCI1_MEM_0_SIZE                     0x00a8
+#define        MV64x60_CPU2PCI1_MEM_1_BASE                     0x00b0
+#define        MV64x60_CPU2PCI1_MEM_1_SIZE                     0x00b8
+#define        MV64x60_CPU2PCI1_MEM_2_BASE                     0x02a0
+#define        MV64x60_CPU2PCI1_MEM_2_SIZE                     0x02a8
+#define        MV64x60_CPU2PCI1_MEM_3_BASE                     0x02b0
+#define        MV64x60_CPU2PCI1_MEM_3_SIZE                     0x02b8
+
+#define        MV64360_CPU2SRAM_BASE                           0x0268
+
+#define        MV64x60_CPU2PCI1_IO_REMAP                       0x0108
+#define        MV64x60_CPU2PCI1_MEM_0_REMAP_LO                 0x0110
+#define        MV64x60_CPU2PCI1_MEM_0_REMAP_HI                 0x0340
+#define        MV64x60_CPU2PCI1_MEM_1_REMAP_LO                 0x0118
+#define        MV64x60_CPU2PCI1_MEM_1_REMAP_HI                 0x0348
+#define        MV64x60_CPU2PCI1_MEM_2_REMAP_LO                 0x0310
+#define        MV64x60_CPU2PCI1_MEM_2_REMAP_HI                 0x0350
+#define        MV64x60_CPU2PCI1_MEM_3_REMAP_LO                 0x0318
+#define        MV64x60_CPU2PCI1_MEM_3_REMAP_HI                 0x0358
+
+/* CPU Control Registers */
+#define MV64x60_CPU_CONFIG                             0x0000
+#define MV64x60_CPU_MODE                               0x0120
+#define MV64x60_CPU_MASTER_CNTL                                0x0160
+#define MV64x60_CPU_XBAR_CNTL_LO                       0x0150
+#define MV64x60_CPU_XBAR_CNTL_HI                       0x0158
+#define MV64x60_CPU_XBAR_TO                            0x0168
+
+#define GT64260_CPU_RR_XBAR_CNTL_LO                    0x0170
+#define GT64260_CPU_RR_XBAR_CNTL_HI                    0x0178
+
+#define MV64360_CPU_PADS_CALIBRATION                   0x03b4
+#define MV64360_CPU_RESET_SAMPLE_LO                    0x03c4
+#define MV64360_CPU_RESET_SAMPLE_HI                    0x03d4
+
+/* SMP Register Map */
+#define MV64360_WHO_AM_I                               0x0200
+#define MV64360_CPU0_DOORBELL                          0x0214
+#define MV64360_CPU0_DOORBELL_CLR                      0x021c
+#define MV64360_CPU0_DOORBELL_MASK                     0x0234
+#define MV64360_CPU1_DOORBELL                          0x0224
+#define MV64360_CPU1_DOORBELL_CLR                      0x022c
+#define MV64360_CPU1_DOORBELL_MASK                     0x023c
+#define MV64360_CPUx_DOORBELL(x)                       (0x0214 + ((x)*0x10))
+#define MV64360_CPUx_DOORBELL_CLR(x)                   (0x021c + ((x)*0x10))
+#define MV64360_CPUx_DOORBELL_MASK(x)                  (0x0234 + ((x)*0x08))
+#define MV64360_SEMAPHORE_0                            0x0244
+#define MV64360_SEMAPHORE_1                            0x024c
+#define MV64360_SEMAPHORE_2                            0x0254
+#define MV64360_SEMAPHORE_3                            0x025c
+#define MV64360_SEMAPHORE_4                            0x0264
+#define MV64360_SEMAPHORE_5                            0x026c
+#define MV64360_SEMAPHORE_6                            0x0274
+#define MV64360_SEMAPHORE_7                            0x027c
+
+/* CPU Sync Barrier Registers */
+#define GT64260_CPU_SYNC_BARRIER_PCI0                  0x00c0
+#define GT64260_CPU_SYNC_BARRIER_PCI1                  0x00c8
+
+#define MV64360_CPU0_SYNC_BARRIER_TRIG                 0x00c0
+#define MV64360_CPU0_SYNC_BARRIER_VIRT                 0x00c8
+#define MV64360_CPU1_SYNC_BARRIER_TRIG                 0x00d0
+#define MV64360_CPU1_SYNC_BARRIER_VIRT                 0x00d8
+
+/* CPU Deadlock and Ordering registers (Rev B part only) */
+#define GT64260_CPU_DEADLOCK_ORDERING                   0x02d0
+#define GT64260_CPU_WB_PRIORITY_BUFFER_DEPTH            0x02d8
+#define GT64260_CPU_COUNTERS_SYNC_BARRIER_ATTRIBUTE     0x02e0
+
+/* CPU Access Protection Registers (gt64260 realy has 8 but don't need) */
+#define        MV64x260_CPU_PROT_WINDOWS                       4
+
+#define        GT64260_CPU_PROT_ACCPROTECT                     (1<<16)
+#define        GT64260_CPU_PROT_WRPROTECT                      (1<<17)
+#define        GT64260_CPU_PROT_CACHEPROTECT                   (1<<18)
+
+#define        MV64360_CPU_PROT_ACCPROTECT                     (1<<20)
+#define        MV64360_CPU_PROT_WRPROTECT                      (1<<21)
+#define        MV64360_CPU_PROT_CACHEPROTECT                   (1<<22)
+#define        MV64360_CPU_PROT_WIN_ENABLE                     (1<<31)
+
+#define MV64x60_CPU_PROT_BASE_0                                0x0180
+#define MV64x60_CPU_PROT_SIZE_0                                0x0188
+#define MV64x60_CPU_PROT_BASE_1                                0x0190
+#define MV64x60_CPU_PROT_SIZE_1                                0x0198
+#define MV64x60_CPU_PROT_BASE_2                                0x01a0
+#define MV64x60_CPU_PROT_SIZE_2                                0x01a8
+#define MV64x60_CPU_PROT_BASE_3                                0x01b0
+#define MV64x60_CPU_PROT_SIZE_3                                0x01b8
+
+#define GT64260_CPU_PROT_BASE_4                                0x01c0
+#define GT64260_CPU_PROT_SIZE_4                                0x01c8
+#define GT64260_CPU_PROT_BASE_5                                0x01d0
+#define GT64260_CPU_PROT_SIZE_5                                0x01d8
+#define GT64260_CPU_PROT_BASE_6                                0x01e0
+#define GT64260_CPU_PROT_SIZE_6                                0x01e8
+#define GT64260_CPU_PROT_BASE_7                                0x01f0
+#define GT64260_CPU_PROT_SIZE_7                                0x01f8
+
+/* CPU Snoop Control Registers (64260 only) */
+#define        GT64260_CPU_SNOOP_WINDOWS                       4
+
+#define        GT64260_CPU_SNOOP_NONE                          0x00000000
+#define        GT64260_CPU_SNOOP_WT                            0x00010000
+#define        GT64260_CPU_SNOOP_WB                            0x00020000
+#define        GT64260_CPU_SNOOP_MASK                          0x00030000
+#define        GT64260_CPU_SNOOP_ALL_BITS                      GT64260_CPU_SNOOP_MASK
+
+#define GT64260_CPU_SNOOP_BASE_0                       0x0380
+#define GT64260_CPU_SNOOP_SIZE_0                       0x0388
+#define GT64260_CPU_SNOOP_BASE_1                       0x0390
+#define GT64260_CPU_SNOOP_SIZE_1                       0x0398
+#define GT64260_CPU_SNOOP_BASE_2                       0x03a0
+#define GT64260_CPU_SNOOP_SIZE_2                       0x03a8
+#define GT64260_CPU_SNOOP_BASE_3                       0x03b0
+#define GT64260_CPU_SNOOP_SIZE_3                       0x03b8
+
+/* CPU Error Report Registers */
+#define MV64x60_CPU_ERR_ADDR_LO                                0x0070
+#define MV64x60_CPU_ERR_ADDR_HI                                0x0078
+#define MV64x60_CPU_ERR_DATA_LO                                0x0128
+#define MV64x60_CPU_ERR_DATA_HI                                0x0130
+#define MV64x60_CPU_ERR_PARITY                         0x0138
+#define MV64x60_CPU_ERR_CAUSE                          0x0140
+#define MV64x60_CPU_ERR_MASK                           0x0148
+
+/*
+ *****************************************************************************
+ *
+ *     SRAM Cotnroller Registers
+ *
+ *****************************************************************************
+ */
+
+#define        MV64360_SRAM_CONFIG                             0x0380
+#define        MV64360_SRAM_TEST_MODE                          0x03f4
+#define        MV64360_SRAM_ERR_CAUSE                          0x0388
+#define        MV64360_SRAM_ERR_ADDR_LO                        0x0390
+#define        MV64360_SRAM_ERR_ADDR_HI                        0x03f8
+#define        MV64360_SRAM_ERR_DATA_LO                        0x0398
+#define        MV64360_SRAM_ERR_DATA_HI                        0x03a0
+#define        MV64360_SRAM_ERR_PARITY                         0x03a8
+
+
+/*
+ *****************************************************************************
+ *
+ *     SDRAM Cotnroller Registers
+ *
+ *****************************************************************************
+ */
+
+/* SDRAM Config Registers (64260) */
+#define        GT64260_SDRAM_CONFIG                            0x0448
+
+/* SDRAM Error Report Registers (64260) */
+#define        GT64260_SDRAM_ERR_DATA_LO                       0x0484
+#define        GT64260_SDRAM_ERR_DATA_HI                       0x0480
+#define        GT64260_SDRAM_ERR_ADDR                          0x0490
+#define        GT64260_SDRAM_ERR_ECC_RCVD                      0x0488
+#define        GT64260_SDRAM_ERR_ECC_CALC                      0x048c
+#define        GT64260_SDRAM_ERR_ECC_CNTL                      0x0494
+#define        GT64260_SDRAM_ERR_ECC_ERR_CNT                   0x0498
+
+/* SDRAM Config Registers (64360) */
+#define        MV64360_SDRAM_CONFIG                            0x1400
+
+/* SDRAM Error Report Registers (64360) */
+#define        MV64360_SDRAM_ERR_DATA_LO                       0x1444
+#define        MV64360_SDRAM_ERR_DATA_HI                       0x1440
+#define        MV64360_SDRAM_ERR_ADDR                          0x1450
+#define        MV64360_SDRAM_ERR_ECC_RCVD                      0x1448
+#define        MV64360_SDRAM_ERR_ECC_CALC                      0x144c
+#define        MV64360_SDRAM_ERR_ECC_CNTL                      0x1454
+#define        MV64360_SDRAM_ERR_ECC_ERR_CNT                   0x1458
+
+
+/*
+ *****************************************************************************
+ *
+ *     Device/BOOT Cotnroller Registers
+ *
+ *****************************************************************************
+ */
+
+/* Device Control Registers */
+#define        MV64x60_DEV_BANK_PARAMS_0                       0x045c
+#define        MV64x60_DEV_BANK_PARAMS_1                       0x0460
+#define        MV64x60_DEV_BANK_PARAMS_2                       0x0464
+#define        MV64x60_DEV_BANK_PARAMS_3                       0x0468
+#define        MV64x60_DEV_BOOT_PARAMS                         0x046c
+#define        MV64x60_DEV_IF_CNTL                             0x04c0
+#define        MV64x60_DEV_IF_XBAR_CNTL_LO                     0x04c8
+#define        MV64x60_DEV_IF_XBAR_CNTL_HI                     0x04cc
+#define        MV64x60_DEV_IF_XBAR_CNTL_TO                     0x04c4
+
+/* Device Interrupt Registers */
+#define        MV64x60_DEV_INTR_CAUSE                          0x04d0
+#define        MV64x60_DEV_INTR_MASK                           0x04d4
+#define        MV64x60_DEV_INTR_ERR_ADDR                       0x04d8
+
+#define        MV64360_DEV_INTR_ERR_DATA                       0x04dc
+#define        MV64360_DEV_INTR_ERR_PAR                        0x04e0
+
+
+/*
+ *****************************************************************************
+ *
+ *     PCI Bridge Interface Registers
+ *
+ *****************************************************************************
+ */
+
+/* PCI Configuration Access Registers */
+#define        MV64x60_PCI0_CONFIG_ADDR                        0x0cf8
+#define        MV64x60_PCI0_CONFIG_DATA                        0x0cfc
+#define        MV64x60_PCI0_IACK                               0x0c34
+
+#define        MV64x60_PCI1_CONFIG_ADDR                        0x0c78
+#define        MV64x60_PCI1_CONFIG_DATA                        0x0c7c
+#define        MV64x60_PCI1_IACK                               0x0cb4
+
+/* PCI Control Registers */
+#define        MV64x60_PCI0_CMD                                0x0c00
+#define        MV64x60_PCI0_MODE                               0x0d00
+#define        MV64x60_PCI0_TO_RETRY                           0x0c04
+#define        MV64x60_PCI0_RD_BUF_DISCARD_TIMER               0x0d04
+#define        MV64x60_PCI0_MSI_TRIGGER_TIMER                  0x0c38
+#define        MV64x60_PCI0_ARBITER_CNTL                       0x1d00
+#define        MV64x60_PCI0_XBAR_CNTL_LO                       0x1d08
+#define        MV64x60_PCI0_XBAR_CNTL_HI                       0x1d0c
+#define        MV64x60_PCI0_XBAR_CNTL_TO                       0x1d04
+#define        MV64x60_PCI0_RD_RESP_XBAR_CNTL_LO               0x1d18
+#define        MV64x60_PCI0_RD_RESP_XBAR_CNTL_HI               0x1d1c
+#define        MV64x60_PCI0_SYNC_BARRIER                       0x1d10
+#define        MV64x60_PCI0_P2P_CONFIG                         0x1d14
+#define        MV64x60_PCI0_INTR_MASK
+
+#define        GT64260_PCI0_P2P_SWAP_CNTL                      0x1d54
+
+#define        MV64x60_PCI1_CMD                                0x0c80
+#define        MV64x60_PCI1_MODE                               0x0d80
+#define        MV64x60_PCI1_TO_RETRY                           0x0c84
+#define        MV64x60_PCI1_RD_BUF_DISCARD_TIMER               0x0d84
+#define        MV64x60_PCI1_MSI_TRIGGER_TIMER                  0x0cb8
+#define        MV64x60_PCI1_ARBITER_CNTL                       0x1d80
+#define        MV64x60_PCI1_XBAR_CNTL_LO                       0x1d88
+#define        MV64x60_PCI1_XBAR_CNTL_HI                       0x1d8c
+#define        MV64x60_PCI1_XBAR_CNTL_TO                       0x1d84
+#define        MV64x60_PCI1_RD_RESP_XBAR_CNTL_LO               0x1d98
+#define        MV64x60_PCI1_RD_RESP_XBAR_CNTL_HI               0x1d9c
+#define        MV64x60_PCI1_SYNC_BARRIER                       0x1d90
+#define        MV64x60_PCI1_P2P_CONFIG                         0x1d94
+
+#define        GT64260_PCI1_P2P_SWAP_CNTL                      0x1dd4
+
+/* PCI Access Control Regions Registers */
+#define        GT64260_PCI_ACC_CNTL_PREFETCHEN                 (1<<12)
+#define        GT64260_PCI_ACC_CNTL_DREADEN                    (1<<13)
+#define        GT64260_PCI_ACC_CNTL_RDPREFETCH                 (1<<16)
+#define        GT64260_PCI_ACC_CNTL_RDLINEPREFETCH             (1<<17)
+#define        GT64260_PCI_ACC_CNTL_RDMULPREFETCH              (1<<18)
+#define        GT64260_PCI_ACC_CNTL_MBURST_32_BTYES            0x00000000
+#define        GT64260_PCI_ACC_CNTL_MBURST_64_BYTES            0x00100000
+#define        GT64260_PCI_ACC_CNTL_MBURST_128_BYTES           0x00200000
+#define        GT64260_PCI_ACC_CNTL_MBURST_MASK                0x00300000
+#define        GT64260_PCI_ACC_CNTL_SWAP_BYTE                  0x00000000
+#define        GT64260_PCI_ACC_CNTL_SWAP_NONE                  0x01000000
+#define        GT64260_PCI_ACC_CNTL_SWAP_BYTE_WORD             0x02000000
+#define        GT64260_PCI_ACC_CNTL_SWAP_WORD                  0x03000000
+#define        GT64260_PCI_ACC_CNTL_SWAP_MASK                  0x03000000
+#define        GT64260_PCI_ACC_CNTL_ACCPROT                    (1<<28)
+#define        GT64260_PCI_ACC_CNTL_WRPROT                     (1<<29)
+
+#define        GT64260_PCI_ACC_CNTL_ALL_BITS   (GT64260_PCI_ACC_CNTL_PREFETCHEN |    \
+                                        GT64260_PCI_ACC_CNTL_DREADEN |       \
+                                        GT64260_PCI_ACC_CNTL_RDPREFETCH |    \
+                                        GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |\
+                                        GT64260_PCI_ACC_CNTL_RDMULPREFETCH | \
+                                        GT64260_PCI_ACC_CNTL_MBURST_MASK |   \
+                                        GT64260_PCI_ACC_CNTL_SWAP_MASK |     \
+                                        GT64260_PCI_ACC_CNTL_ACCPROT|        \
+                                        GT64260_PCI_ACC_CNTL_WRPROT)
+
+#define        MV64360_PCI_ACC_CNTL_ENABLE                     (1<<0)
+#define        MV64360_PCI_ACC_CNTL_REQ64                      (1<<1)
+#define        MV64360_PCI_ACC_CNTL_SNOOP_NONE                 0x00000000
+#define        MV64360_PCI_ACC_CNTL_SNOOP_WT                   0x00000004
+#define        MV64360_PCI_ACC_CNTL_SNOOP_WB                   0x00000008
+#define        MV64360_PCI_ACC_CNTL_SNOOP_MASK                 0x0000000c
+#define        MV64360_PCI_ACC_CNTL_ACCPROT                    (1<<4)
+#define        MV64360_PCI_ACC_CNTL_WRPROT                     (1<<5)
+#define        MV64360_PCI_ACC_CNTL_SWAP_BYTE                  0x00000000
+#define        MV64360_PCI_ACC_CNTL_SWAP_NONE                  0x00000040
+#define        MV64360_PCI_ACC_CNTL_SWAP_BYTE_WORD             0x00000080
+#define        MV64360_PCI_ACC_CNTL_SWAP_WORD                  0x000000c0
+#define        MV64360_PCI_ACC_CNTL_SWAP_MASK                  0x000000c0
+#define        MV64360_PCI_ACC_CNTL_MBURST_32_BYTES            0x00000000
+#define        MV64360_PCI_ACC_CNTL_MBURST_64_BYTES            0x00000100
+#define        MV64360_PCI_ACC_CNTL_MBURST_128_BYTES           0x00000200
+#define        MV64360_PCI_ACC_CNTL_MBURST_MASK                0x00000300
+#define        MV64360_PCI_ACC_CNTL_RDSIZE_32_BYTES            0x00000000
+#define        MV64360_PCI_ACC_CNTL_RDSIZE_64_BYTES            0x00000400
+#define        MV64360_PCI_ACC_CNTL_RDSIZE_128_BYTES           0x00000800
+#define        MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES           0x00000c00
+#define        MV64360_PCI_ACC_CNTL_RDSIZE_MASK                0x00000c00
+
+#define        MV64360_PCI_ACC_CNTL_ALL_BITS   (MV64360_PCI_ACC_CNTL_ENABLE |      \
+                                        MV64360_PCI_ACC_CNTL_REQ64 |       \
+                                        MV64360_PCI_ACC_CNTL_SNOOP_MASK |  \
+                                        MV64360_PCI_ACC_CNTL_ACCPROT |     \
+                                        MV64360_PCI_ACC_CNTL_WRPROT |      \
+                                        MV64360_PCI_ACC_CNTL_SWAP_MASK |   \
+                                        MV64360_PCI_ACC_CNTL_MBURST_MASK | \
+                                        MV64360_PCI_ACC_CNTL_RDSIZE_MASK)
+
+#define        MV64x60_PCI0_ACC_CNTL_0_BASE_LO                 0x1e00
+#define        MV64x60_PCI0_ACC_CNTL_0_BASE_HI                 0x1e04
+#define        MV64x60_PCI0_ACC_CNTL_0_SIZE                    0x1e08
+#define        MV64x60_PCI0_ACC_CNTL_1_BASE_LO                 0x1e10
+#define        MV64x60_PCI0_ACC_CNTL_1_BASE_HI                 0x1e14
+#define        MV64x60_PCI0_ACC_CNTL_1_SIZE                    0x1e18
+#define        MV64x60_PCI0_ACC_CNTL_2_BASE_LO                 0x1e20
+#define        MV64x60_PCI0_ACC_CNTL_2_BASE_HI                 0x1e24
+#define        MV64x60_PCI0_ACC_CNTL_2_SIZE                    0x1e28
+#define        MV64x60_PCI0_ACC_CNTL_3_BASE_LO                 0x1e30
+#define        MV64x60_PCI0_ACC_CNTL_3_BASE_HI                 0x1e34
+#define        MV64x60_PCI0_ACC_CNTL_3_SIZE                    0x1e38
+#define        MV64x60_PCI0_ACC_CNTL_4_BASE_LO                 0x1e40
+#define        MV64x60_PCI0_ACC_CNTL_4_BASE_HI                 0x1e44
+#define        MV64x60_PCI0_ACC_CNTL_4_SIZE                    0x1e48
+#define        MV64x60_PCI0_ACC_CNTL_5_BASE_LO                 0x1e50
+#define        MV64x60_PCI0_ACC_CNTL_5_BASE_HI                 0x1e54
+#define        MV64x60_PCI0_ACC_CNTL_5_SIZE                    0x1e58
+
+#define        GT64260_PCI0_ACC_CNTL_6_BASE_LO                 0x1e60
+#define        GT64260_PCI0_ACC_CNTL_6_BASE_HI                 0x1e64
+#define        GT64260_PCI0_ACC_CNTL_6_SIZE                    0x1e68
+#define        GT64260_PCI0_ACC_CNTL_7_BASE_LO                 0x1e70
+#define        GT64260_PCI0_ACC_CNTL_7_BASE_HI                 0x1e74
+#define        GT64260_PCI0_ACC_CNTL_7_SIZE                    0x1e78
+
+#define        MV64x60_PCI1_ACC_CNTL_0_BASE_LO                 0x1e80
+#define        MV64x60_PCI1_ACC_CNTL_0_BASE_HI                 0x1e84
+#define        MV64x60_PCI1_ACC_CNTL_0_SIZE                    0x1e88
+#define        MV64x60_PCI1_ACC_CNTL_1_BASE_LO                 0x1e90
+#define        MV64x60_PCI1_ACC_CNTL_1_BASE_HI                 0x1e94
+#define        MV64x60_PCI1_ACC_CNTL_1_SIZE                    0x1e98
+#define        MV64x60_PCI1_ACC_CNTL_2_BASE_LO                 0x1ea0
+#define        MV64x60_PCI1_ACC_CNTL_2_BASE_HI                 0x1ea4
+#define        MV64x60_PCI1_ACC_CNTL_2_SIZE                    0x1ea8
+#define        MV64x60_PCI1_ACC_CNTL_3_BASE_LO                 0x1eb0
+#define        MV64x60_PCI1_ACC_CNTL_3_BASE_HI                 0x1eb4
+#define        MV64x60_PCI1_ACC_CNTL_3_SIZE                    0x1eb8
+#define        MV64x60_PCI1_ACC_CNTL_4_BASE_LO                 0x1ec0
+#define        MV64x60_PCI1_ACC_CNTL_4_BASE_HI                 0x1ec4
+#define        MV64x60_PCI1_ACC_CNTL_4_SIZE                    0x1ec8
+#define        MV64x60_PCI1_ACC_CNTL_5_BASE_LO                 0x1ed0
+#define        MV64x60_PCI1_ACC_CNTL_5_BASE_HI                 0x1ed4
+#define        MV64x60_PCI1_ACC_CNTL_5_SIZE                    0x1ed8
+
+#define        GT64260_PCI1_ACC_CNTL_6_BASE_LO                 0x1ee0
+#define        GT64260_PCI1_ACC_CNTL_6_BASE_HI                 0x1ee4
+#define        GT64260_PCI1_ACC_CNTL_6_SIZE                    0x1ee8
+#define        GT64260_PCI1_ACC_CNTL_7_BASE_LO                 0x1ef0
+#define        GT64260_PCI1_ACC_CNTL_7_BASE_HI                 0x1ef4
+#define        GT64260_PCI1_ACC_CNTL_7_SIZE                    0x1ef8
+
+/* PCI Snoop Control Registers (64260 only) */
+#define        GT64260_PCI_SNOOP_NONE                          0x00000000
+#define        GT64260_PCI_SNOOP_WT                            0x00001000
+#define        GT64260_PCI_SNOOP_WB                            0x00002000
+
+#define        GT64260_PCI0_SNOOP_0_BASE_LO                    0x1f00
+#define        GT64260_PCI0_SNOOP_0_BASE_HI                    0x1f04
+#define        GT64260_PCI0_SNOOP_0_SIZE                       0x1f08
+#define        GT64260_PCI0_SNOOP_1_BASE_LO                    0x1f10
+#define        GT64260_PCI0_SNOOP_1_BASE_HI                    0x1f14
+#define        GT64260_PCI0_SNOOP_1_SIZE                       0x1f18
+#define        GT64260_PCI0_SNOOP_2_BASE_LO                    0x1f20
+#define        GT64260_PCI0_SNOOP_2_BASE_HI                    0x1f24
+#define        GT64260_PCI0_SNOOP_2_SIZE                       0x1f28
+#define        GT64260_PCI0_SNOOP_3_BASE_LO                    0x1f30
+#define        GT64260_PCI0_SNOOP_3_BASE_HI                    0x1f34
+#define        GT64260_PCI0_SNOOP_3_SIZE                       0x1f38
+
+#define        GT64260_PCI1_SNOOP_0_BASE_LO                    0x1f80
+#define        GT64260_PCI1_SNOOP_0_BASE_HI                    0x1f84
+#define        GT64260_PCI1_SNOOP_0_SIZE                       0x1f88
+#define        GT64260_PCI1_SNOOP_1_BASE_LO                    0x1f90
+#define        GT64260_PCI1_SNOOP_1_BASE_HI                    0x1f94
+#define        GT64260_PCI1_SNOOP_1_SIZE                       0x1f98
+#define        GT64260_PCI1_SNOOP_2_BASE_LO                    0x1fa0
+#define        GT64260_PCI1_SNOOP_2_BASE_HI                    0x1fa4
+#define        GT64260_PCI1_SNOOP_2_SIZE                       0x1fa8
+#define        GT64260_PCI1_SNOOP_3_BASE_LO                    0x1fb0
+#define        GT64260_PCI1_SNOOP_3_BASE_HI                    0x1fb4
+#define        GT64260_PCI1_SNOOP_3_SIZE                       0x1fb8
+
+/* PCI Error Report Registers */
+#define MV64x60_PCI0_ERR_SERR_MASK                     0x0c28
+#define MV64x60_PCI0_ERR_ADDR_LO                       0x1d40
+#define MV64x60_PCI0_ERR_ADDR_HI                       0x1d44
+#define MV64x60_PCI0_ERR_DATA_LO                       0x1d48
+#define MV64x60_PCI0_ERR_DATA_HI                       0x1d4c
+#define MV64x60_PCI0_ERR_CMD                           0x1d50
+#define MV64x60_PCI0_ERR_CAUSE                         0x1d58
+#define MV64x60_PCI0_ERR_MASK                          0x1d5c
+
+#define MV64x60_PCI1_ERR_SERR_MASK                     0x0ca8
+#define MV64x60_PCI1_ERR_ADDR_LO                       0x1dc0
+#define MV64x60_PCI1_ERR_ADDR_HI                       0x1dc4
+#define MV64x60_PCI1_ERR_DATA_LO                       0x1dc8
+#define MV64x60_PCI1_ERR_DATA_HI                       0x1dcc
+#define MV64x60_PCI1_ERR_CMD                           0x1dd0
+#define MV64x60_PCI1_ERR_CAUSE                         0x1dd8
+#define MV64x60_PCI1_ERR_MASK                          0x1ddc
+
+/* PCI Slave Address Decoding Registers */
+#define        MV64x60_PCI0_MEM_0_SIZE                         0x0c08
+#define        MV64x60_PCI0_MEM_1_SIZE                         0x0d08
+#define        MV64x60_PCI0_MEM_2_SIZE                         0x0c0c
+#define        MV64x60_PCI0_MEM_3_SIZE                         0x0d0c
+#define        MV64x60_PCI1_MEM_0_SIZE                         0x0c88
+#define        MV64x60_PCI1_MEM_1_SIZE                         0x0d88
+#define        MV64x60_PCI1_MEM_2_SIZE                         0x0c8c
+#define        MV64x60_PCI1_MEM_3_SIZE                         0x0d8c
+
+#define        MV64x60_PCI0_BAR_ENABLE                         0x0c3c
+#define        MV64x60_PCI1_BAR_ENABLE                         0x0cbc
+
+#define        MV64x60_PCI0_PCI_DECODE_CNTL                    0x0d3c
+
+
+
+
+
+#define        MV64x60_PCI0_SLAVE_BAR_REG_ENABLES              0x0c3c
+#define        MV64x60_PCI0_SLAVE_MEM_0_REMAP                  0x0c48
+#define        MV64x60_PCI0_SLAVE_MEM_1_REMAP                  0x0d48
+#define        MV64x60_PCI0_SLAVE_MEM_2_REMAP                  0x0c4c
+#define        MV64x60_PCI0_SLAVE_MEM_3_REMAP                  0x0d4c
+#define        MV64x60_PCI0_SLAVE_CS_0_REMAP                   0x0c50
+#define        MV64x60_PCI0_SLAVE_CS_1_REMAP                   0x0d50
+#define        MV64x60_PCI0_SLAVE_CS_2_REMAP                   0x0d58
+#define        MV64x60_PCI0_SLAVE_CS_3_REMAP                   0x0c54
+#define        MV64x60_PCI0_SLAVE_BOOT_REMAP                   0x0d54
+#define        MV64x60_PCI0_SLAVE_P2P_MEM_0_REMAP_LO           0x0d5c
+#define        MV64x60_PCI0_SLAVE_P2P_MEM_0_REMAP_HI           0x0d60
+#define        MV64x60_PCI0_SLAVE_P2P_MEM_1_REMAP_LO           0x0d64
+#define        MV64x60_PCI0_SLAVE_P2P_MEM_1_REMAP_HI           0x0d68
+#define        MV64x60_PCI0_SLAVE_P2P_IO_REMAP                 0x0d6c
+#define        MV64x60_PCI0_SLAVE_CPU_REMAP                    0x0d70
+
+#define        GT64260_PCI0_SLAVE_DAC_SCS_0_REMAP              0x0f00
+#define        GT64260_PCI0_SLAVE_DAC_SCS_1_REMAP              0x0f04
+#define        GT64260_PCI0_SLAVE_DAC_SCS_2_REMAP              0x0f08
+#define        GT64260_PCI0_SLAVE_DAC_SCS_3_REMAP              0x0f0c
+#define        GT64260_PCI0_SLAVE_DAC_CS_0_REMAP               0x0f10
+#define        GT64260_PCI0_SLAVE_DAC_CS_1_REMAP               0x0f14
+#define        GT64260_PCI0_SLAVE_DAC_CS_2_REMAP               0x0f18
+#define        GT64260_PCI0_SLAVE_DAC_CS_3_REMAP               0x0f1c
+#define        GT64260_PCI0_SLAVE_DAC_BOOT_REMAP               0x0f20
+#define        GT64260_PCI0_SLAVE_DAC_P2P_MEM_0_REMAP_LO       0x0f24
+#define        GT64260_PCI0_SLAVE_DAC_P2P_MEM_0_REMAP_HI       0x0f28
+#define        GT64260_PCI0_SLAVE_DAC_P2P_MEM_1_REMAP_LO       0x0f2c
+#define        GT64260_PCI0_SLAVE_DAC_P2P_MEM_1_REMAP_HI       0x0f30
+#define        GT64260_PCI0_SLAVE_DAC_CPU_REMAP                0x0f34
+
+#define        GT64260_PCI0_SLAVE_EXP_ROM_REMAP                0x0f38
+#define        GT64260_PCI0_SLAVE_PCI_DECODE_CNTL              0x0d3c
+
+
+
+
+
+/* XXXX BEGIN */
+#define        MV64x60_PCI1_PCI_DECODE_CNTL                    0x0dbc
+
+#define        MV64x60_PCI1_SLAVE_MEM_0_SIZE                   0x0c88
+#define        MV64x60_PCI1_SLAVE_MEM_1_SIZE                   0x0d88
+#define        MV64x60_PCI1_SLAVE_MEM_2_SIZE                   0x0c8c
+#define        MV64x60_PCI1_SLAVE_MEM_3_SIZE                   0x0d8c
+#define        MV64x60_PCI1_SLAVE_CS_0_SIZE                    0x0c90
+#define        MV64x60_PCI1_SLAVE_CS_1_SIZE                    0x0d90
+#define        MV64x60_PCI1_SLAVE_CS_2_SIZE                    0x0d98
+#define        MV64x60_PCI1_SLAVE_CS_3_SIZE                    0x0c94
+#define        MV64x60_PCI1_SLAVE_BOOT_SIZE                    0x0d94
+#define        MV64x60_PCI1_SLAVE_P2P_MEM_0_SIZE               0x0d9c
+#define        MV64x60_PCI1_SLAVE_P2P_MEM_1_SIZE               0x0da0
+#define        MV64x60_PCI1_SLAVE_P2P_IO_SIZE                  0x0da4
+#define        MV64x60_PCI1_SLAVE_CPU_SIZE                     0x0da8
+
+
+/* XXXXX END */
+
+
+#define        GT64260_PCI1_SLAVE_DAC_SCS_0_SIZE               0x0e80
+#define        GT64260_PCI1_SLAVE_DAC_SCS_1_SIZE               0x0e84
+#define        GT64260_PCI1_SLAVE_DAC_SCS_2_SIZE               0x0e88
+#define        GT64260_PCI1_SLAVE_DAC_SCS_3_SIZE               0x0e8c
+#define        GT64260_PCI1_SLAVE_DAC_CS_0_SIZE                0x0e90
+#define        GT64260_PCI1_SLAVE_DAC_CS_1_SIZE                0x0e94
+#define        GT64260_PCI1_SLAVE_DAC_CS_2_SIZE                0x0e98
+#define        GT64260_PCI1_SLAVE_DAC_CS_3_SIZE                0x0e9c
+#define        GT64260_PCI1_SLAVE_DAC_BOOT_SIZE                0x0ea0
+#define        GT64260_PCI1_SLAVE_DAC_P2P_MEM_0_SIZE           0x0ea4
+#define        GT64260_PCI1_SLAVE_DAC_P2P_MEM_1_SIZE           0x0ea8
+#define        GT64260_PCI1_SLAVE_DAC_CPU_SIZE                 0x0eac
+
+#define        GT64260_PCI1_SLAVE_EXP_ROM_SIZE                 0x0dac
+
+
+
+
+/* XXXX BEGIN */
+
+#define        MV64x60_PCI1_SLAVE_BAR_REG_ENABLES              0x0cbc
+#define        MV64x60_PCI1_SLAVE_MEM_0_REMAP                  0x0cc8
+#define        MV64x60_PCI1_SLAVE_MEM_1_REMAP                  0x0dc8
+#define        MV64x60_PCI1_SLAVE_MEM_2_REMAP                  0x0ccc
+#define        MV64x60_PCI1_SLAVE_MEM_3_REMAP                  0x0dcc
+#define        MV64x60_PCI1_SLAVE_CS_0_REMAP                   0x0cd0
+#define        MV64x60_PCI1_SLAVE_CS_1_REMAP                   0x0dd0
+#define        MV64x60_PCI1_SLAVE_CS_2_REMAP                   0x0dd8
+#define        MV64x60_PCI1_SLAVE_CS_3_REMAP                   0x0cd4
+#define        MV64x60_PCI1_SLAVE_BOOT_REMAP                   0x0dd4
+#define        MV64x60_PCI1_SLAVE_P2P_MEM_0_REMAP_LO           0x0ddc
+#define        MV64x60_PCI1_SLAVE_P2P_MEM_0_REMAP_HI           0x0de0
+#define        MV64x60_PCI1_SLAVE_P2P_MEM_1_REMAP_LO           0x0de4
+#define        MV64x60_PCI1_SLAVE_P2P_MEM_1_REMAP_HI           0x0de8
+#define        MV64x60_PCI1_SLAVE_P2P_IO_REMAP                 0x0dec
+#define        MV64x60_PCI1_SLAVE_CPU_REMAP                    0x0df0
+
+/* XXXXX END */
+
+
+
+#define        GT64260_PCI1_SLAVE_DAC_SCS_0_REMAP              0x0f80
+#define        GT64260_PCI1_SLAVE_DAC_SCS_1_REMAP              0x0f84
+#define        GT64260_PCI1_SLAVE_DAC_SCS_2_REMAP              0x0f88
+#define        GT64260_PCI1_SLAVE_DAC_SCS_3_REMAP              0x0f8c
+#define        GT64260_PCI1_SLAVE_DAC_CS_0_REMAP               0x0f90
+#define        GT64260_PCI1_SLAVE_DAC_CS_1_REMAP               0x0f94
+#define        GT64260_PCI1_SLAVE_DAC_CS_2_REMAP               0x0f98
+#define        GT64260_PCI1_SLAVE_DAC_CS_3_REMAP               0x0f9c
+#define        GT64260_PCI1_SLAVE_DAC_BOOT_REMAP               0x0fa0
+#define        GT64260_PCI1_SLAVE_DAC_P2P_MEM_0_REMAP_LO       0x0fa4
+#define        GT64260_PCI1_SLAVE_DAC_P2P_MEM_0_REMAP_HI       0x0fa8
+#define        GT64260_PCI1_SLAVE_DAC_P2P_MEM_1_REMAP_LO       0x0fac
+#define        GT64260_PCI1_SLAVE_DAC_P2P_MEM_1_REMAP_HI       0x0fb0
+#define        GT64260_PCI1_SLAVE_DAC_CPU_REMAP                0x0fb4
+
+#define        GT64260_PCI1_SLAVE_EXP_ROM_REMAP                0x0fb8
+#define        GT64260_PCI1_SLAVE_PCI_DECODE_CNTL              0x0dbc
+
+
+/*
+ *****************************************************************************
+ *
+ *     Timer/Counter Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        MV64x60_TIMR_CNTR_0                             0x0850
+#define        MV64x60_TIMR_CNTR_1                             0x0854
+#define        MV64x60_TIMR_CNTR_2                             0x0858
+#define        MV64x60_TIMR_CNTR_3                             0x085c
+#define        MV64x60_TIMR_CNTR_0_3_CNTL                      0x0864
+#define        MV64x60_TIMR_CNTR_0_3_INTR_CAUSE                0x0868
+#define        MV64x60_TIMR_CNTR_0_3_INTR_MASK                 0x086c
+
+#define        GT64260_TIMR_CNTR_4                             0x0950
+#define        GT64260_TIMR_CNTR_5                             0x0954
+#define        GT64260_TIMR_CNTR_6                             0x0958
+#define        GT64260_TIMR_CNTR_7                             0x095c
+#define        GT64260_TIMR_CNTR_4_7_CNTL                      0x0964
+#define        GT64260_TIMR_CNTR_4_7_INTR_CAUSE                0x0968
+#define        GT64260_TIMR_CNTR_4_7_INTR_MASK                 0x096c
+
+
+/*
+ *****************************************************************************
+ *
+ *     Communications Controller (Enet, Serial, etc.) Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        GT64260_COMM_ENET_0_OFFSET              0xf200
+#define        GT64260_COMM_ENET_1_OFFSET              0xf220
+#define        GT64260_COMM_ENET_2_OFFSET              0xf240
+
+#define        GT64260_ENET_CNTL_LO                    \
+       (0xf200 - GT64260_COMM_ENET_0_BASE)
+#define        GT64260_ENET_CNTL_HI                    \
+       (0xf204 - GT64260_COMM_ENET_0_BASE)
+#define        GT64260_ENET_RX_BUF_PCI_ADDR_HI         \
+       (0xf208 - GT64260_COMM_ENET_0_BASE)
+#define        GT64260_ENET_TX_BUF_PCI_ADDR_HI         \
+       (0xf20c - GT64260_COMM_ENET_0_BASE)
+#define        GT64260_ENET_RX_DESC_ADDR_HI            \
+       (0xf210 - GT64260_COMM_ENET_0_BASE)
+#define        GT64260_ENET_TX_DESC_ADDR_HI            \
+       (0xf214 - GT64260_COMM_ENET_0_BASE)
+#define        GT64260_ENET_HASH_TAB_PCI_ADDR_HI       \
+       (0xf218 - GT64260_COMM_ENET_0_BASE)
+
+#define        GT64260_COMM_MPSC_0_OFFSET              0xf280
+#define        GT64260_COMM_MPSC_1_OFFSET              0xf2c0
+
+#define        GT64260_MPSC_CNTL_LO                    \
+       (0xf280 - GT64260_COMM_MPSC_0_BASE)
+#define        GT64260_MPSC_CNTL_HI                    \
+       (0xf284 - GT64260_COMM_MPSC_0_BASE)
+#define        GT64260_MPSC_RX_BUF_PCI_ADDR_HI \
+       (0xf288 - GT64260_COMM_MPSC_0_BASE)
+#define        GT64260_MPSC_TX_BUF_PCI_ADDR_HI \
+       (0xf28c - GT64260_COMM_MPSC_0_BASE)
+#define        GT64260_MPSC_RX_DESC_ADDR_HI            \
+       (0xf290 - GT64260_COMM_MPSC_0_BASE)
+#define        GT64260_MPSC_TX_DESC_ADDR_HI            \
+       (0xf294 - GT64260_COMM_MPSC_0_BASE)
+
+#define        GT64260_SER_INIT_PCI_ADDR_HI            0xf320
+#define        GT64260_SER_INIT_LAST_DATA              0xf324
+#define        GT64260_SER_INIT_CONTROL                0xf328
+#define        GT64260_SER_INIT_STATUS                 0xf32c
+
+#define        GT64260_COMM_ARBITER_CNTL               0xf300
+#define        GT64260_COMM_CONFIG                     0xb40c
+#define        GT64260_COMM_XBAR_TO                    0xf304
+#define        GT64260_COMM_INTR_CAUSE                 0xf310
+#define        GT64260_COMM_INTR_MASK                  0xf314
+#define        GT64260_COMM_ERR_ADDR                   0xf318
+
+
+/*
+ *****************************************************************************
+ *
+ *     Fast Ethernet Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        GT64260_ENET_PHY_ADDR                   0x2000
+#define        GT64260_ENET_ESMIR                      0x2010
+
+#define GT64260_ENET_0_OFFSET                  0x2400
+#define GT64260_ENET_1_OFFSET                  0x2800
+#define GT64260_ENET_2_OFFSET                  0x2c00
+
+#define        GT64260_ENET_EPCR                       (0x2400 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EPCXR                      (0x2408 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EPCMR                      (0x2410 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EPSR                       (0x2418 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ESPR                       (0x2420 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EHTPR                      (0x2428 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EFCSAL                     (0x2430 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EFCSAH                     (0x2438 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ESDCR                      (0x2440 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ESDCMR                     (0x2448 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EICR                       (0x2450 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EIMR                       (0x2458 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EFRDP0                     (0x2480 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EFRDP1                     (0x2484 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EFRDP2                     (0x2488 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_EFRDP3                     (0x248c - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ECRDP0                     (0x24a0 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ECRDP1                     (0x24a4 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ECRDP2                     (0x24a8 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ECRDP3                     (0x24ac - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ECTDP0                     (0x24e0 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_ECTDP1                     (0x24e4 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_DSCP2P0L                   (0x2460 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_DSCP2P0H                   (0x2464 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_DSCP2P1L                   (0x2468 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_DSCP2P1H                   (0x246c - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_VPT2P                      (0x2470 - GT64260_ENET_0_OFFSET)
+#define        GT64260_ENET_MIB_CTRS                   (0x2500 - GT64260_ENET_0_OFFSET)
+
+/*
+ *****************************************************************************
+ *
+ *     IDMA Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        GT64260_IDMA_0_OFFSET                   0x0800
+#define        GT64260_IDMA_1_OFFSET                   0x0804
+#define        GT64260_IDMA_2_OFFSET                   0x0808
+#define        GT64260_IDMA_3_OFFSET                   0x080c
+#define        GT64260_IDMA_4_OFFSET                   0x0900
+#define        GT64260_IDMA_5_OFFSET                   0x0904
+#define        GT64260_IDMA_6_OFFSET                   0x0908
+#define        GT64260_IDMA_7_OFFSET                   0x090c
+
+#define        GT64260_IDMA_BYTE_COUNT                 (0x0800 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_SRC_ADDR                   (0x0810 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_DST_ADDR                   (0x0820 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_NEXT_DESC                  (0x0830 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_CUR_DESC                   (0x0870 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_SRC_PCI_ADDR_HI            (0x0890 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_DST_PCI_ADDR_HI            (0x08a0 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_NEXT_DESC_PCI_ADDR_HI      (0x08b0 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_CONTROL_LO                 (0x0840 - GT64260_IDMA_0_OFFSET)
+#define        GT64260_IDMA_CONTROL_HI                 (0x0880 - GT64260_IDMA_0_OFFSET)
+
+#define        GT64260_IDMA_0_3_ARBITER_CNTL           0x0860
+#define        GT64260_IDMA_4_7_ARBITER_CNTL           0x0960
+
+#define        GT64260_IDMA_0_3_XBAR_TO                0x08d0
+#define        GT64260_IDMA_4_7_XBAR_TO                0x09d0
+
+#define        GT64260_IDMA_0_3_INTR_CAUSE             0x08c0
+#define        GT64260_IDMA_0_3_INTR_MASK              0x08c4
+#define        GT64260_IDMA_0_3_ERROR_ADDR             0x08c8
+#define        GT64260_IDMA_0_3_ERROR_SELECT           0x08cc
+#define        GT64260_IDMA_4_7_INTR_CAUSE             0x09c0
+#define        GT64260_IDMA_4_7_INTR_MASK              0x09c4
+#define        GT64260_IDMA_4_7_ERROR_ADDR             0x09c8
+#define        GT64260_IDMA_4_7_ERROR_SELECT           0x09cc
+
+/*
+ *****************************************************************************
+ *
+ *     Watchdog Timer Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        GT64260_WDT_WDC                                 0xb410
+#define        GT64260_WDT_WDV                                 0xb414
+
+
+/*
+ *****************************************************************************
+ *
+ *      General Purpose Pins Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        MV64x60_GPP_IO_CNTL                             0xf100
+#define        MV64x60_GPP_LEVEL_CNTL                          0xf110
+#define        MV64x60_GPP_VALUE                               0xf104
+#define        MV64x60_GPP_INTR_CAUSE                          0xf108
+#define        MV64x60_GPP_INTR_MASK                           0xf10c
+
+
+/*
+ *****************************************************************************
+ *
+ *     Multi-Purpose Pins Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        MV64x60_MPP_CNTL_0                              0xf000
+#define        MV64x60_MPP_CNTL_1                              0xf004
+#define        MV64x60_MPP_CNTL_2                              0xf008
+#define        MV64x60_MPP_CNTL_3                              0xf00c
+#define        GT64260_MPP_SERIAL_PORTS_MULTIPLEX              0xf010
+
+
+/*
+ *****************************************************************************
+ *
+ *     I2C Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define GT64260_I2C_OFFSET                     0xc000
+
+#define GT64260_I2C_ADDR                       (0xc000 - GT64260_I2C_OFFSET)
+#define GT64260_I2C_EX_ADDR                    (0xc010 - GT64260_I2C_OFFSET)
+#define GT64260_I2C_DATA                       (0xc004 - GT64260_I2C_OFFSET)
+#define GT64260_I2C_CONTROL                    (0xc008 - GT64260_I2C_OFFSET)
+#define GT64260_I2C_STATUS                     (0xc00c - GT64260_I2C_OFFSET)
+#define GT64260_I2C_BAUD_RATE                  (0xc00c - GT64260_I2C_OFFSET)
+#define GT64260_I2C_RESET                      (0xc01c - GT64260_I2C_OFFSET)
+
+#define GT64260_I2C_ACK_BIT                    (1<<2)
+#define GT64260_I2C_IFLG_BIT                   (1<<3)
+#define GT64260_I2C_STOP_BIT                   (1<<4)
+#define GT64260_I2C_START_BIT                  (1<<5)
+#define GT64260_I2C_ENABLE_BIT                 (1<<6)
+#define GT64260_I2C_INT_ENABLE_BIT             (1<<7)
+
+#define GT64260_I2C_DATA_READ_BIT              0x01
+
+#define GT64260_I2C_STATUS_SENT_START          0x08
+#define GT64260_I2C_STATUS_RESENT_START                0x10
+#define GT64260_I2C_STATUS_WRITE_ADDR_ACK      0x18
+#define GT64260_I2C_STATUS_WRITE_ACK           0x28
+#define GT64260_I2C_STATUS_READ_ADDR_ACK       0x40
+#define GT64260_I2C_STATUS_READ_ACK            0x50
+#define GT64260_I2C_STATUS_READ_NO_ACK         0x58
+#define GT64260_I2C_STATUS_IDLE                        0xf8
+
+
+/*
+ *****************************************************************************
+ *
+ *     Interrupt Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        GT64260_IC_OFFSET                               0x0c18
+
+#define        GT64260_IC_MAIN_CAUSE_LO                (0x0c18 - GT64260_IC_OFFSET)
+#define        GT64260_IC_MAIN_CAUSE_HI                (0x0c68 - GT64260_IC_OFFSET)
+#define        GT64260_IC_CPU_INTR_MASK_LO             (0x0c1c - GT64260_IC_OFFSET)
+#define        GT64260_IC_CPU_INTR_MASK_HI             (0x0c6c - GT64260_IC_OFFSET)
+#define        GT64260_IC_CPU_SELECT_CAUSE             (0x0c70 - GT64260_IC_OFFSET)
+#define        GT64260_IC_PCI0_INTR_MASK_LO            (0x0c24 - GT64260_IC_OFFSET)
+#define        GT64260_IC_PCI0_INTR_MASK_HI            (0x0c64 - GT64260_IC_OFFSET)
+#define        GT64260_IC_PCI0_SELECT_CAUSE            (0x0c74 - GT64260_IC_OFFSET)
+#define        GT64260_IC_PCI1_INTR_MASK_LO            (0x0ca4 - GT64260_IC_OFFSET)
+#define        GT64260_IC_PCI1_INTR_MASK_HI            (0x0ce4 - GT64260_IC_OFFSET)
+#define        GT64260_IC_PCI1_SELECT_CAUSE            (0x0cf4 - GT64260_IC_OFFSET)
+#define        GT64260_IC_CPU_INT_0_MASK               (0x0e60 - GT64260_IC_OFFSET)
+#define        GT64260_IC_CPU_INT_1_MASK               (0x0e64 - GT64260_IC_OFFSET)
+#define        GT64260_IC_CPU_INT_2_MASK               (0x0e68 - GT64260_IC_OFFSET)
+#define        GT64260_IC_CPU_INT_3_MASK               (0x0e6c - GT64260_IC_OFFSET)
+
+#define        MV64360_IC_OFFSET                               0x0000
+
+#define        MV64360_IC_MAIN_CAUSE_LO                (0x0004 - MV64360_IC_OFFSET)
+#define        MV64360_IC_MAIN_CAUSE_HI                (0x000c - MV64360_IC_OFFSET)
+#define        MV64360_IC_CPU0_INTR_MASK_LO            (0x0014 - MV64360_IC_OFFSET)
+#define        MV64360_IC_CPU0_INTR_MASK_HI            (0x001c - MV64360_IC_OFFSET)
+#define        MV64360_IC_CPU0_SELECT_CAUSE            (0x0024 - MV64360_IC_OFFSET)
+#define        MV64360_IC_CPU1_INTR_MASK_LO            (0x0034 - MV64360_IC_OFFSET)
+#define        MV64360_IC_CPU1_INTR_MASK_HI            (0x003c - MV64360_IC_OFFSET)
+#define        MV64360_IC_CPU1_SELECT_CAUSE            (0x0044 - MV64360_IC_OFFSET)
+#define        MV64360_IC_INT0_MASK_LO                 (0x0054 - MV64360_IC_OFFSET)
+#define        MV64360_IC_INT0_MASK_HI                 (0x005c - MV64360_IC_OFFSET)
+#define        MV64360_IC_INT0_SELECT_CAUSE            (0x0064 - MV64360_IC_OFFSET)
+#define        MV64360_IC_INT1_MASK_LO                 (0x0074 - MV64360_IC_OFFSET)
+#define        MV64360_IC_INT1_MASK_HI                 (0x007c - MV64360_IC_OFFSET)
+#define        MV64360_IC_INT1_SELECT_CAUSE            (0x0084 - MV64360_IC_OFFSET)
+
+#endif /* __ASMPPC_MV64x60_DEFS_H */
diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h
new file mode 100644 (file)
index 0000000..d522d43
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * For boards with physically mapped flash and using 
+ * drivers/mtd/maps/physmap.c mapping driver.
+ *
+ * $Id: physmap.h,v 1.2 2004/07/14 17:48:46 dwmw2 Exp $
+ *
+ * Copyright (C) 2003 MontaVista Software Inc.
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MTD_PHYSMAP__
+
+#include <linux/config.h>
+
+#if defined(CONFIG_MTD_PHYSMAP) 
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+/*
+ * The map_info for physmap.  Board can override size, buswidth, phys,
+ * (*set_vpp)(), etc in their initial setup routine.
+ */
+extern struct map_info physmap_map;
+
+/*
+ * Board needs to specify the exact mapping during their setup time.
+ */
+static inline void physmap_configure(unsigned long addr, unsigned long size, int buswidth, void (*set_vpp)(struct map_info *, int) )
+{
+       physmap_map.phys = addr;
+       physmap_map.size = size;
+       physmap_map.buswidth = buswidth;
+       physmap_map.set_vpp = set_vpp;
+}
+
+#if defined(CONFIG_MTD_PARTITIONS)
+
+/*
+ * Machines that wish to do flash partition may want to call this function in 
+ * their setup routine.  
+ *
+ *     physmap_set_partitions(mypartitions, num_parts);
+ *
+ * Note that one can always override this hard-coded partition with 
+ * command line partition (you need to enable CONFIG_MTD_CMDLINE_PARTS).
+ */
+void physmap_set_partitions(struct mtd_partition *parts, int num_parts);
+
+#endif /* defined(CONFIG_MTD_PARTITIONS) */
+#endif /* defined(CONFIG_MTD) */
+
+#endif /* __LINUX_MTD_PHYSMAP__ */
+
diff --git a/include/mtd/inftl-user.h b/include/mtd/inftl-user.h
new file mode 100644 (file)
index 0000000..bda4f2c
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * $Id: inftl-user.h,v 1.1 2004/05/05 15:17:00 dwmw2 Exp $
+ *
+ * Parts of INFTL headers shared with userspace 
+ *
+ */
+
+#ifndef __MTD_INFTL_USER_H__
+#define __MTD_INFTL_USER_H__
+
+#define        OSAK_VERSION    0x5120
+#define        PERCENTUSED     98
+
+#define        SECTORSIZE      512
+
+/* Block Control Information */
+
+struct inftl_bci {
+       uint8_t ECCsig[6];
+       uint8_t Status;
+       uint8_t Status1;
+} __attribute__((packed));
+
+struct inftl_unithead1 {
+       uint16_t virtualUnitNo;
+       uint16_t prevUnitNo;
+       uint8_t ANAC;
+       uint8_t NACs;
+       uint8_t parityPerField;
+       uint8_t discarded;
+} __attribute__((packed));
+
+struct inftl_unithead2 {
+       uint8_t parityPerField;
+       uint8_t ANAC;
+       uint16_t prevUnitNo;
+       uint16_t virtualUnitNo;
+       uint8_t NACs;
+       uint8_t discarded;
+} __attribute__((packed));
+
+struct inftl_unittail {
+       uint8_t Reserved[4];
+       uint16_t EraseMark;
+       uint16_t EraseMark1;
+} __attribute__((packed));
+
+union inftl_uci {
+       struct inftl_unithead1 a;
+       struct inftl_unithead2 b;
+       struct inftl_unittail c;
+};
+
+struct inftl_oob {
+       struct inftl_bci b;
+       union inftl_uci u;
+};
+
+
+/* INFTL Media Header */
+
+struct INFTLPartition {
+       __u32 virtualUnits;
+       __u32 firstUnit;
+       __u32 lastUnit;
+       __u32 flags;
+       __u32 spareUnits;
+       __u32 Reserved0;
+       __u32 Reserved1;
+} __attribute__((packed));
+
+struct INFTLMediaHeader {
+       char bootRecordID[8];
+       __u32 NoOfBootImageBlocks;
+       __u32 NoOfBinaryPartitions;
+       __u32 NoOfBDTLPartitions;
+       __u32 BlockMultiplierBits;
+       __u32 FormatFlags;
+       __u32 OsakVersion;
+       __u32 PercentUsed;
+       struct INFTLPartition Partitions[4];
+} __attribute__((packed));
+
+/* Partition flag types */
+#define        INFTL_BINARY    0x20000000
+#define        INFTL_BDTL      0x40000000
+#define        INFTL_LAST      0x80000000
+
+#endif /* __MTD_INFTL_USER_H__ */
+
+
diff --git a/include/mtd/jffs2-user.h b/include/mtd/jffs2-user.h
new file mode 100644 (file)
index 0000000..d508ef0
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $
+ *
+ * JFFS2 definitions for use in user space only
+ */
+
+#ifndef __JFFS2_USER_H__
+#define __JFFS2_USER_H__
+
+/* This file is blessed for inclusion by userspace */
+#include <linux/jffs2.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#undef cpu_to_je16
+#undef cpu_to_je32
+#undef cpu_to_jemode
+#undef je16_to_cpu
+#undef je32_to_cpu
+#undef jemode_to_cpu
+
+extern int target_endian;
+
+#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
+#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
+
+#define cpu_to_je16(x) ((jint16_t){t16(x)})
+#define cpu_to_je32(x) ((jint32_t){t32(x)})
+#define cpu_to_jemode(x) ((jmode_t){t32(x)})
+
+#define je16_to_cpu(x) (t16((x).v16))
+#define je32_to_cpu(x) (t32((x).v32))
+#define jemode_to_cpu(x) (t32((x).m))
+
+#endif /* __JFFS2_USER_H__ */
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h
new file mode 100644 (file)
index 0000000..db5fca2
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * $Id: mtd-abi.h,v 1.5 2004/06/22 09:29:35 gleixner Exp $
+ *
+ * Portions of MTD ABI definition which are shared by kernel and user space 
+ */
+
+#ifndef __MTD_ABI_H__
+#define __MTD_ABI_H__
+
+struct erase_info_user {
+       uint32_t start;
+       uint32_t length;
+};
+
+struct mtd_oob_buf {
+       uint32_t start;
+       uint32_t length;
+       unsigned char *ptr;
+};
+
+#define MTD_ABSENT             0
+#define MTD_RAM                        1
+#define MTD_ROM                        2
+#define MTD_NORFLASH           3
+#define MTD_NANDFLASH          4
+#define MTD_PEROM              5
+#define MTD_OTHER              14
+#define MTD_UNKNOWN            15
+
+#define MTD_CLEAR_BITS         1       // Bits can be cleared (flash)
+#define MTD_SET_BITS           2       // Bits can be set
+#define MTD_ERASEABLE          4       // Has an erase function
+#define MTD_WRITEB_WRITEABLE   8       // Direct IO is possible
+#define MTD_VOLATILE           16      // Set for RAMs
+#define MTD_XIP                        32      // eXecute-In-Place possible
+#define MTD_OOB                        64      // Out-of-band data (NAND flash)
+#define MTD_ECC                        128     // Device capable of automatic ECC
+
+// Some common devices / combinations of capabilities
+#define MTD_CAP_ROM            0
+#define MTD_CAP_RAM            (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
+#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
+#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
+#define MTD_WRITEABLE          (MTD_CLEAR_BITS|MTD_SET_BITS)
+
+
+// Types of automatic ECC/Checksum available
+#define MTD_ECC_NONE           0       // No automatic ECC available
+#define MTD_ECC_RS_DiskOnChip  1       // Automatic ECC on DiskOnChip
+#define MTD_ECC_SW             2       // SW ECC for Toshiba & Samsung devices
+
+/* ECC byte placement */
+#define MTD_NANDECC_OFF                0       // Switch off ECC (Not recommended)
+#define MTD_NANDECC_PLACE      1       // Use the given placement in the structure (YAFFS1 legacy mode)
+#define MTD_NANDECC_AUTOPLACE  2       // Use the default placement scheme
+#define MTD_NANDECC_PLACEONLY  3       // Use the given placement in the structure (Do not store ecc result on read)
+
+struct mtd_info_user {
+       uint8_t type;
+       uint32_t flags;
+       uint32_t size;   // Total size of the MTD
+       uint32_t erasesize;
+       uint32_t oobblock;  // Size of OOB blocks (e.g. 512)
+       uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
+       uint32_t ecctype;
+       uint32_t eccsize;
+};
+
+struct region_info_user {
+       uint32_t offset;                /* At which this region starts, 
+                                        * from the beginning of the MTD */
+       uint32_t erasesize;             /* For this region */
+       uint32_t numblocks;             /* Number of blocks in this region */
+       uint32_t regionindex;
+};
+
+#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
+#define MEMERASE                _IOW('M', 2, struct erase_info_user)
+#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
+#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
+#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
+#define MEMUNLOCK               _IOW('M', 6, struct erase_info_user)
+#define MEMGETREGIONCOUNT      _IOR('M', 7, int)
+#define MEMGETREGIONINFO       _IOWR('M', 8, struct region_info_user)
+#define MEMSETOOBSEL           _IOW('M', 9, struct nand_oobinfo)
+#define MEMGETOOBSEL           _IOR('M', 10, struct nand_oobinfo)
+#define MEMGETBADBLOCK         _IOW('M', 11, loff_t)
+#define MEMSETBADBLOCK         _IOW('M', 12, loff_t)
+
+struct nand_oobinfo {
+       uint32_t useecc;
+       uint32_t eccbytes;
+       uint32_t oobfree[8][2];
+       uint32_t eccpos[32];
+};
+
+#endif /* __MTD_ABI_H__ */
diff --git a/include/mtd/nftl-user.h b/include/mtd/nftl-user.h
new file mode 100644 (file)
index 0000000..924ec04
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * $Id: nftl-user.h,v 1.1 2004/05/05 14:44:57 dwmw2 Exp $
+ *
+ * Parts of NFTL headers shared with userspace 
+ *
+ */
+
+#ifndef __MTD_NFTL_USER_H__
+#define __MTD_NFTL_USER_H__
+
+/* Block Control Information */
+
+struct nftl_bci {
+       unsigned char ECCSig[6];
+       uint8_t Status;
+       uint8_t Status1;
+}__attribute__((packed));
+
+/* Unit Control Information */
+
+struct nftl_uci0 {
+       uint16_t VirtUnitNum;
+       uint16_t ReplUnitNum;
+       uint16_t SpareVirtUnitNum;
+       uint16_t SpareReplUnitNum;
+} __attribute__((packed));
+
+struct nftl_uci1 {
+       uint32_t WearInfo;
+       uint16_t EraseMark;
+       uint16_t EraseMark1;
+} __attribute__((packed));
+
+struct nftl_uci2 {
+        uint16_t FoldMark;
+        uint16_t FoldMark1;
+       uint32_t unused;
+} __attribute__((packed));
+
+union nftl_uci {
+       struct nftl_uci0 a;
+       struct nftl_uci1 b;
+       struct nftl_uci2 c;
+};
+
+struct nftl_oob {
+       struct nftl_bci b;
+       union nftl_uci u;
+};
+
+/* NFTL Media Header */
+
+struct NFTLMediaHeader {
+       char DataOrgID[6];
+       uint16_t NumEraseUnits;
+       uint16_t FirstPhysicalEUN;
+       uint32_t FormattedSize;
+       unsigned char UnitSizeFactor;
+} __attribute__((packed));
+
+#define MAX_ERASE_ZONES (8192 - 512)
+
+#define ERASE_MARK 0x3c69
+#define SECTOR_FREE 0xff
+#define SECTOR_USED 0x55
+#define SECTOR_IGNORE 0x11
+#define SECTOR_DELETED 0x00
+
+#define FOLD_MARK_IN_PROGRESS 0x5555
+
+#define ZONE_GOOD 0xff
+#define ZONE_BAD_ORIGINAL 0
+#define ZONE_BAD_MARKED 7
+
+
+#endif /* __MTD_NFTL_USER_H__ */