--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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)
+{
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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, ®);
+ 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 = >64260a_ci;
+ break;
+
+ case MV64x60_TYPE_GT64260B:
+ bh->ci = >64260b_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;
+}
--- /dev/null
+/*
+ * 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
+ }
+};
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/**
+ *
+ * $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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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(®s->imask, IMASK_INIT_CLEAR);
+
+ /* Clear all interrupts */
+ gfar_write(®s->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(®s->maccfg1);
+ tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN);
+ gfar_write(®s->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(®s->tbase),
+ sizeof(struct txbd)*priv->tx_ring_size,
+ DMA_BIDIRECTIONAL);
+ dma_unmap_single(NULL, gfar_read(®s->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(®s->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(®s->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(®s->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(®s->txic,
+ mk_ic_value(priv->txcount, priv->txtime));
+ else
+ gfar_write(®s->txic, 0);
+
+ if (priv->rxcoalescing)
+ gfar_write(®s->rxic,
+ mk_ic_value(priv->rxcount, priv->rxtime));
+ else
+ gfar_write(®s->rxic, 0);
+
+ init_waitqueue_head(&priv->rxcleanupq);
+
+ /* Enable Rx and Tx in MACCFG1 */
+ tempval = gfar_read(®s->maccfg1);
+ tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
+ gfar_write(®s->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(®s->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(®s->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(®s->maccfg2);
+ tempval &= ~(MACCFG2_FULL_DUPLEX);
+ gfar_write(®s->maccfg2, tempval);
+
+ printk(KERN_INFO "%s: Half Duplex\n",
+ dev->name);
+ } else {
+ tempval = gfar_read(®s->maccfg2);
+ tempval |= MACCFG2_FULL_DUPLEX;
+ gfar_write(®s->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(®s->maccfg2);
+ tempval =
+ ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
+ gfar_write(®s->maccfg2, tempval);
+ break;
+ case 100:
+ case 10:
+ tempval = gfar_read(®s->maccfg2);
+ tempval =
+ ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
+ gfar_write(®s->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(®s->rctrl);
+ tempval |= RCTRL_PROM;
+ gfar_write(®s->rctrl, tempval);
+ } else {
+ /* Set RCTRL to not PROM */
+ tempval = gfar_read(®s->rctrl);
+ tempval &= ~(RCTRL_PROM);
+ gfar_write(®s->rctrl, tempval);
+ }
+
+ if(dev->flags & IFF_ALLMULTI) {
+ /* Set the hash to rx all multicast frames */
+ gfar_write(®s->gaddr0, 0xffffffff);
+ gfar_write(®s->gaddr1, 0xffffffff);
+ gfar_write(®s->gaddr2, 0xffffffff);
+ gfar_write(®s->gaddr3, 0xffffffff);
+ gfar_write(®s->gaddr4, 0xffffffff);
+ gfar_write(®s->gaddr5, 0xffffffff);
+ gfar_write(®s->gaddr6, 0xffffffff);
+ gfar_write(®s->gaddr7, 0xffffffff);
+ } else {
+ /* zero out the hash */
+ gfar_write(®s->gaddr0, 0x0);
+ gfar_write(®s->gaddr1, 0x0);
+ gfar_write(®s->gaddr2, 0x0);
+ gfar_write(®s->gaddr3, 0x0);
+ gfar_write(®s->gaddr4, 0x0);
+ gfar_write(®s->gaddr5, 0x0);
+ gfar_write(®s->gaddr6, 0x0);
+ gfar_write(®s->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 = ®s->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);
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+
+ /* Write out the value we want */
+ gfar_write(®base->miimcon, value);
+
+ /* Wait for the transaction to finish */
+ while (gfar_read(®base->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(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+
+ /* Clear miimcom, and then initiate a read */
+ gfar_write(®base->miimcom, 0);
+ gfar_write(®base->miimcom, MIIM_READ_COMMAND);
+
+ /* Wait for the transaction to finish */
+ while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
+ cpu_relax();
+
+ /* Grab the value of the register from miimstat */
+ value = gfar_read(®base->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++;
+ }
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#
+# Make file for the Marvell MPSC driver.
+#
+
+obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
+obj-$(CONFIG_PPC32) += mpsc_ppc32.o
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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__ */
+
--- /dev/null
+/*
+ * $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__ */
+
+
--- /dev/null
+/*
+ * $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__ */
--- /dev/null
+/*
+ * $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__ */
--- /dev/null
+/*
+ * $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__ */