From: Marc Fiuczynski Date: Fri, 23 Jul 2004 15:20:08 +0000 (+0000) Subject: Initial revision X-Git-Tag: before-ipod-patch~6 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=3a4462546b364b69356e55dae047bdd5abb9526f;p=linux-2.6.git Initial revision --- diff --git a/arch/ppc/boot/simple/misc-mv64x60.S b/arch/ppc/boot/simple/misc-mv64x60.S new file mode 100644 index 000000000..9c809c84f --- /dev/null +++ b/arch/ppc/boot/simple/misc-mv64x60.S @@ -0,0 +1,57 @@ +/* + * arch/ppc/boot/simple/misc-mv64x60.S + * + * Code to change the base address of the host bridges and call board specific + * init routine. + * + * Author: Mark Greer + * + * 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 +#include +#include +#include +#include + + .globl mv64x60_init +mv64x60_init: + mflr r27 + +#if (CONFIG_MV64X60_NEW_BASE != CONFIG_MV64X60_BASE) + bl move_base +#endif + bl mv64x60_board_init + + mtlr r27 + blr + +#if (CONFIG_MV64X60_NEW_BASE != CONFIG_MV64X60_BASE) +move_base: + li r20,0 + li r23,20 + + /* Relocate bridge's regs */ + addis r25,0,CONFIG_MV64X60_BASE@h + ori r25,r25,MV64x60_INTERNAL_SPACE_DECODE + lwbrx r26,0,(r25) + lis r24,0xffff + and r26,r26,r24 + addis r24,0,CONFIG_MV64X60_NEW_BASE@h + srw r24,r24,r23 + or r26,r26,r24 + stwbrx r26,0,(r25) + sync + + /* Wait for write to take effect */ + addis r25,0,CONFIG_MV64X60_NEW_BASE@h + ori r25,r25,MV64x60_INTERNAL_SPACE_DECODE +1: lwbrx r24,0,(r25) + cmpw r24,r26 + bne 1b + + blr +#endif diff --git a/arch/ppc/boot/simple/mv64x60_stub.c b/arch/ppc/boot/simple/mv64x60_stub.c new file mode 100644 index 000000000..46d99245e --- /dev/null +++ b/arch/ppc/boot/simple/mv64x60_stub.c @@ -0,0 +1,20 @@ +/* + * arch/ppc/boot/simple/mv64x60_stub.c + * + * Stub for board_init() routine called from mv64x60_init(). + * + * Author: Mark A. Greer + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +long mv64x60_console_baud = 9600; /* Default baud: 9600 */ +long mv64x60_mpsc_clk_src = 8; /* Default clk src: TCLK */ +long mv64x60_mpsc_clk_freq = 100000000; /* Default clk freq: 100 MHz */ + +void +mv64x60_board_init(void) +{ +} diff --git a/arch/ppc/boot/simple/mv64x60_tty.c b/arch/ppc/boot/simple/mv64x60_tty.c new file mode 100644 index 000000000..b1cb21a16 --- /dev/null +++ b/arch/ppc/boot/simple/mv64x60_tty.c @@ -0,0 +1,331 @@ +/* + * arch/ppc/boot/simple/mv64x60_tty.c + * + * Bootloader version of the embedded MPSC/UART driver for the Marvell 64x60. + * Note: Due to a GT64260A erratum, DMA will be used for UART input (via SDMA). + * + * Author: Mark A. Greer + * + * 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 +#include +#include +#include +#include +#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= TX_NUM_DESC) cur_td[com_port] = 0; + + *(unchar *)(tdp->buffer ^ 7) = c; + tdp->bytecnt = 1; + tdp->shadow = 1; + tdp->cmd_stat = SDMA_DESC_CMDSTAT_L | SDMA_DESC_CMDSTAT_F | + SDMA_DESC_CMDSTAT_O; + + MV64x60_REG_WRITE(sdma_regs[com_port].sctdp, tdp); + MV64x60_REG_WRITE(sdma_regs[com_port].sftdp, tdp); + MV64x60_REG_WRITE(sdma_regs[com_port].sdcm, + MV64x60_REG_READ(sdma_regs[com_port].sdcm) | SDMA_SDCM_TXD); + + return; +} + +unsigned char +serial_getc(unsigned long com_port) +{ + mv64x60_rx_desc_t *rdp; + unchar c = '\0'; + + rdp = &rd[com_port][cur_rd[com_port]]; + + if ((rdp->cmd_stat & (SDMA_DESC_CMDSTAT_O|SDMA_DESC_CMDSTAT_ES)) == 0) { + c = *(unchar *)(rdp->buffer ^ 7); + RX_INIT_RDP(rdp); + if (++cur_rd[com_port] >= RX_NUM_DESC) cur_rd[com_port] = 0; + } + + return c; +} + +int +serial_tstc(unsigned long com_port) +{ + mv64x60_rx_desc_t *rdp; + int loop_count = 0; + int rc = 0; + + rdp = &rd[com_port][cur_rd[com_port]]; + + /* Go thru rcv desc's until empty looking for one with data (no error)*/ + while (((rdp->cmd_stat & SDMA_DESC_CMDSTAT_O) == 0) && + (loop_count++ < RX_NUM_DESC)) { + + /* If there was an error, reinit the desc & continue */ + if ((rdp->cmd_stat & SDMA_DESC_CMDSTAT_ES) != 0) { + RX_INIT_RDP(rdp); + if (++cur_rd[com_port] >= RX_NUM_DESC) { + cur_rd[com_port] = 0; + } + rdp = (mv64x60_rx_desc_t *)rdp->next_desc_ptr; + } + else { + rc = 1; + break; + } + } + + return rc; +} + +void +serial_close(unsigned long com_port) +{ + stop_dma(com_port); + return; +} diff --git a/arch/ppc/platforms/dmv182.c b/arch/ppc/platforms/dmv182.c new file mode 100644 index 000000000..dd57e1605 --- /dev/null +++ b/arch/ppc/platforms/dmv182.c @@ -0,0 +1,707 @@ +/* + * arch/ppc/platforms/dmv182p.c + * Setup code for the Dy-4 SVME/DMV-182 + * + * Copyright (C) 2004 TimeSys Corporation + * Copyright (C) 2004 Red Hat, Inc. + * + * Original 2.4 port by Scott Wood + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for linux/serial_core.h */ +#include +#include + +#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 +#include + +#define dmv182_board_io_phys 0xe0000000 +#define dmv182_board_io_size 0x00040000 + +#ifdef __BOOTER__ +#define dmv182_board_io_virt ((u8 *)dmv182_board_io_phys) +#else +#define dmv182_board_io_virt ((u8 *)0xf0000000) +#endif + +#define dmv182_fpga_io (dmv182_board_io_virt + 0x10000) +#define dmv182_rtc (dmv182_board_io_virt + 0x20000) +#define dmv182_nvram (dmv182_board_io_virt + 0x30000) + +// This has to go above the mv64360 interrupts, as even though +// the mv64360 code can handle relocating its interrupt range, +// the device drivers themselves are oblivious to this. + +#define DMV182_IRQ_TEMPA 96 +#define DMV182_IRQ_TEMPB 97 +#define DMV182_IRQ_TEMPC 98 +#define DMV182_IRQ_TEMPD 99 +#define DMV182_IRQ_PMC1A 100 +#define DMV182_IRQ_PMC1B 101 +#define DMV182_IRQ_PMC1C 102 +#define DMV182_IRQ_PMC1D 103 +#define DMV182_IRQ_PMC2A 104 +#define DMV182_IRQ_PMC2B 105 +#define DMV182_IRQ_PMC2C 106 +#define DMV182_IRQ_PMC2D 107 +#define DMV182_IRQ_ENET_PHY2 108 +#define DMV182_IRQ_ENET_PHY1 109 +#define DMV182_IRQ_IPM0 110 +#define DMV182_IRQ_IPM1 111 +#define DMV182_IRQ_USB_A 112 +#define DMV182_IRQ_USB_B 113 +#define DMV182_IRQ_USB_C 114 +#define DMV182_IRQ_USB_SMI 115 +#define DMV182_IRQ_RTC 116 +#define DMV182_IRQ_WDOG_CPU0 117 +#define DMV182_IRQ_WDOG_CPU1 118 +#define DMV182_IRQ_TIMER0_CPU0 120 +#define DMV182_IRQ_TIMER1_CPU0 121 +#define DMV182_IRQ_TIMER2_CPU0 122 +#define DMV182_IRQ_TIMER0_CPU1 123 +#define DMV182_IRQ_TIMER1_CPU1 124 +#define DMV182_IRQ_TIMER2_CPU1 125 +#define DMV182_IRQ_SERIAL_CH1 126 +#define DMV182_IRQ_SERIAL_CH2 127 +#define DMV182_IRQ_VME_CPU0 128 +#define DMV182_IRQ_VME_CPU1 129 + +// 28 FPGA interrupts starting from here +#define DMV182_IRQ_FPGA 132 + +#endif diff --git a/arch/ppc/platforms/dmv182_serial.h b/arch/ppc/platforms/dmv182_serial.h new file mode 100644 index 000000000..b1dbb2d1c --- /dev/null +++ b/arch/ppc/platforms/dmv182_serial.h @@ -0,0 +1,19 @@ +#ifndef __DMV182_SERIAL_H +#define __DMV182_SERIAL_H + +#include +#include + +#define BASE_BAUD (36864000 / 16) +#define RS_TABLE_SIZE 2 + +#define STD_UART_OP(num) \ + { .baud_base = BASE_BAUD, \ + .irq = DMV182_IRQ_SERIAL_CH##num, \ + .flags = ASYNC_SKIP_TEST | ASYNC_BUGGY_UART, \ + .iomem_base = dmv182_fpga_io + 0x18 + 8 * (num - 1), \ + .io_type = SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS STD_UART_OP(1) STD_UART_OP(2) + +#endif diff --git a/arch/ppc/platforms/ev64260.c b/arch/ppc/platforms/ev64260.c new file mode 100644 index 000000000..127588904 --- /dev/null +++ b/arch/ppc/platforms/ev64260.c @@ -0,0 +1,893 @@ +/* + * arch/ppc/platforms/ev64260.c + * + * Board setup routines for the Marvell/Galileo EV-64260-BP Evaluation Board. + * + * Author: Mark A. Greer + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(CONFIG_SERIAL_MPSC_CONSOLE) +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; idef->additions; + + dp->max_idle = 40; /* XXXX what should this be? */ + dp->default_baud = EV64260_DEFAULT_BAUD; + dp->brg_clk_src = EV64260_MPSC_CLK_SRC; + dp->brg_clk_freq = EV64260_MPSC_CLK_FREQ; + } + + if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 1)) + != NULL) { + dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions; + + dp->max_idle = 40; /* XXXX what should this be? */ + dp->default_baud = 9600; /* XXXX */ + dp->brg_clk_src = EV64260_MPSC_CLK_SRC; + dp->brg_clk_freq = EV64260_MPSC_CLK_FREQ; + } +#endif + + return; +} + +static void __init +ev64260_setup_arch(void) +{ + if ( ppc_md.progress ) + ppc_md.progress("ev64260_setup_arch: enter", 0); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("ev64260_setup_arch: Enabling L2 cache", 0); + + /* Enable L2 and L3 caches (if 745x) */ + _set_L2CR(_get_L2CR() | L2CR_L2E); + _set_L3CR(_get_L3CR() | L3CR_L3E); + + if ( ppc_md.progress ) + ppc_md.progress("ev64260_setup_arch: Initializing bridge", 0); + + ev64260_setup_bridge(); /* set up PCI bridge(s) */ + ev64260_setup_peripherals(); /* set up chip selects/GPP/MPP etc */ + + if ( ppc_md.progress ) + ppc_md.progress("ev64260_setup_arch: bridge init complete", 0); + + /* Set OCP values to reflect this board's setup */ + ev64260_fixup_ocp(); + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_MPSC_CONSOLE) + ev64260_early_serial_map(); +#endif + + printk(BOARD_VENDOR " " BOARD_MACHINE "\n"); + printk("EV-64260-BP port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + if ( ppc_md.progress ) + ppc_md.progress("ev64260_setup_arch: exit", 0); + + return; +} + +static void +ev64260_reset_board(void *addr) +{ + local_irq_disable(); + + /* disable and invalidate the L2 cache */ + _set_L2CR(0); + _set_L2CR(0x200000); + + /* flush and disable L1 I/D cache */ + __asm__ __volatile__ + ("mfspr 3,1008\n\t" + "ori 5,5,0xcc00\n\t" + "ori 4,3,0xc00\n\t" + "andc 5,3,5\n\t" + "sync\n\t" + "mtspr 1008,4\n\t" + "isync\n\t" + "sync\n\t" + "mtspr 1008,5\n\t" + "isync\n\t" + "sync\n\t"); + + /* unmap any other random cs's that might overlap with bootcs */ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, 0, 0, 0); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, 0, 0, 0); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, 0, 0, 0); + mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN, 0, 0, 0); + + /* map bootrom back in to gt @ reset defaults */ + mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, + 0xff800000, 8*1024*1024, 0); + + /* move gt reg base back to default, setup default pci0 swapping + * config... */ + mv64x60_write(&bh, MV64x60_INTERNAL_SPACE_DECODE, + (1<<24) | MV64x60_INTERNAL_SPACE_DEFAULT_ADDR>>20); + + /* NOTE: FROM NOW ON no more GT_REGS accesses.. 0x1 is not mapped + * via BAT or MMU, and MSR IR/DR is ON */ +#if 0 + /* BROKEN... IR/DR is still on !! won't work!! */ + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + out_8((u_char *)EV64260_BOARD_MODRST_REG, 0x01); +#else + /* SRR0 has system reset vector, SRR1 has default MSR value */ + /* rfi restores MSR from SRR1 and sets the PC to the SRR0 value */ + /* NOTE: assumes reset vector is at 0xfff00100 */ + __asm__ __volatile__ + ("mtspr 26, %0\n\t" + "li 4,(1<<6)\n\t" + "mtspr 27,4\n\t" + "rfi\n\t" + :: "r" (addr):"r4"); +#endif + return; +} + +static void +ev64260_restart(char *cmd) +{ + volatile ulong i = 10000000; + + ev64260_reset_board((void *)0xfff00100); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +ev64260_halt(void) +{ + local_irq_disable(); + while (1); + /* NOTREACHED */ +} + +static void +ev64260_power_off(void) +{ + ev64260_halt(); + /* NOTREACHED */ +} + +static int +ev64260_show_cpuinfo(struct seq_file *m) +{ + uint pvid; + + pvid = mfspr(PVR); + seq_printf(m, "vendor\t\t: " BOARD_VENDOR "\n"); + seq_printf(m, "machine\t\t: " BOARD_MACHINE "\n"); + seq_printf(m, "cpu MHz\t\t: %d\n", ev64260_get_cpu_speed()/1000/1000); + seq_printf(m, "bus MHz\t\t: %d\n", ev64260_get_bus_speed()/1000/1000); + + return 0; +} + +/* DS1501 RTC has too much variation to use RTC for calibration */ +static void __init +ev64260_calibrate_decr(void) +{ + ulong freq; + + freq = ev64260_get_bus_speed()/4; + + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000); + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + return; +} + +#if 0 /* XXXX */ +#ifdef CONFIG_USE_PPCBOOT +static void parse_ppcbootinfo(unsigned long r3, + unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + bd_t *bd=NULL; + char *cmdline_start=NULL; + int cmdline_len=0; + + if(r3) { + if((r3 & 0xf0000000) == 0) r3 += KERNELBASE; + if((r3 & 0xf0000000) == KERNELBASE) { + bd=(void *)r3; + + /* hack for ppcboot loaders that report freqs in Mhz */ + if(bd->bi_intfreq<1000000) bd->bi_intfreq*=1000000; + if(bd->bi_busfreq<1000000) bd->bi_busfreq*=1000000; + + memcpy(&ppcboot_bd,bd,sizeof(ppcboot_bd)); + ppcboot_bd_valid=1; + } + } + +#ifdef CONFIG_BLK_DEV_INITRD + if(r4 && r5 && r5>r4) { + if((r4 & 0xf0000000) == 0) r4 += KERNELBASE; + if((r5 & 0xf0000000) == 0) r5 += KERNELBASE; + if((r4 & 0xf0000000) == KERNELBASE) { + initrd_start=r4; + initrd_end=r5; + initrd_below_start_ok = 1; + } + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + if(r6 && r7 && r7>r6) { + if((r6 & 0xf0000000) == 0) r6 += KERNELBASE; + if((r7 & 0xf0000000) == 0) r7 += KERNELBASE; + if((r6 & 0xf0000000) == KERNELBASE) { + cmdline_start=(void *)r6; + cmdline_len=(r7-r6); + strncpy(cmd_line,cmdline_start,cmdline_len); + } + } + + if(ppcboot_bd_valid) { + printk("found bd_t @%p\n", bd); + printk("memstart=%08lx\n", bd->bi_memstart); + printk("memsize=%08lx\n", bd->bi_memsize); + printk("enetaddr=%02x%02x%02x%02x%02x%02x\n", + bd->bi_enetaddr[0], + bd->bi_enetaddr[1], + bd->bi_enetaddr[2], + bd->bi_enetaddr[3], + bd->bi_enetaddr[4], + bd->bi_enetaddr[5] + ); + printk("intfreq=%ld\n", bd->bi_intfreq); + printk("busfreq=%ld\n", bd->bi_busfreq); + printk("baudrate=%ld\n", bd->bi_baudrate); + } + +#ifdef CONFIG_BLK_DEV_INITRD + if(initrd_start) { + printk("found initrd @%lx-%lx\n", initrd_start, initrd_end); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + if(cmdline_start && cmdline_len) { + printk("found cmdline: '%s'\n", cmd_line); + } +} +#endif /* USE PPC_BOOT */ +#endif + +#if 0 /* XXXX */ +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +static int +ev64260_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +static void +ev64260_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); + return; +} + +static void +ev64260_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); + return; +} + +static void __init +ev64260_ide_pci_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + struct pci_dev *dev; +#if 1 /* NTL */ + int i; + + //printk("regs %d to %d @ 0x%x\n", IDE_DATA_OFFSET, IDE_STATUS_OFFSET, data_port); + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = data_port; + data_port++; + } + + //printk("ctrl %d @ 0x%x\n", IDE_CONTROL_OFFSET, ctrl_port); + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; +#endif + + pci_for_each_dev(dev) { + if (((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) || + ((dev->class >> 8) == PCI_CLASS_STORAGE_RAID)) { + hw->irq = dev->irq; + + if (irq != NULL) { + *irq = dev->irq; + } + } + } + + return; +} +#endif +#endif + +#if !defined(CONFIG_USE_PPCBOOT) +/* + * Set BAT 3 to map 0xfb000000 to 0xfc000000 of physical memory space. + */ +static __inline__ void +ev64260_set_bat(void) +{ + mb(); + mtspr(DBAT1U, 0xfb0001fe); + mtspr(DBAT1L, 0xfb00002a); + mb(); + + return; +} +#endif + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) +static void __init +ev64260_map_io(void) +{ + io_block_mapping(0xfb000000, 0xfb000000, 0x01000000, _PAGE_IO); +} +#endif + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_BLK_DEV_INITRD + extern int initrd_below_start_ok; + + initrd_start=initrd_end=0; + initrd_below_start_ok=0; +#endif /* CONFIG_BLK_DEV_INITRD */ + + ppcboot_bd_valid=0; + memset(&ppcboot_bd,0,sizeof(ppcboot_bd)); + +#ifdef CONFIG_USE_PPCBOOT + parse_ppcbootinfo(r3, r4, r5, r6, r7); +#else + parse_bootinfo(find_bootinfo()); +#endif + + isa_mem_base = 0; + isa_io_base = 0xa0000000; /* XXXX */ + pci_dram_offset = 0x80000000; /* XXXX */ + + loops_per_jiffy = ev64260_get_cpu_speed() / HZ; + + ppc_md.setup_arch = ev64260_setup_arch; + ppc_md.show_cpuinfo = ev64260_show_cpuinfo; + ppc_md.init_IRQ = gt64260_init_irq; + ppc_md.get_irq = gt64260_get_irq; + + ppc_md.pcibios_fixup = ev64260_pci_fixups; + + ppc_md.restart = ev64260_restart; + ppc_md.power_off = ev64260_power_off; + ppc_md.halt = ev64260_halt; + + ppc_md.find_end_of_memory = ev64260_find_end_of_memory; + + ppc_md.init = NULL; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + + ppc_md.calibrate_decr = ev64260_calibrate_decr; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.ide_init_hwif = ev64260_ide_pci_init_hwif_ports; +#endif + + bh.p_base = CONFIG_MV64X60_NEW_BASE; + +#if !defined(CONFIG_USE_PPCBOOT) + ev64260_set_bat(); +#endif + +#ifdef CONFIG_SERIAL_8250 +#if defined(CONFIG_SERIAL_TEXT_DEBUG) + ppc_md.setup_io_mappings = ev64260_map_io; + ppc_md.progress = gen550_progress; +#endif +#if defined(CONFIG_KGDB) + ppc_md.setup_io_mappings = ev64260_map_io; + ppc_md.early_serial_map = ev64260_early_serial_map; +#endif +#elif defined(CONFIG_SERIAL_MPSC_CONSOLE) +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.setup_io_mappings = ev64260_map_io; + ppc_md.progress = gt64260_mpsc_progress; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#ifdef CONFIG_KGDB + ppc_md.setup_io_mappings = ev64260_map_io; + ppc_md.early_serial_map = ev64260_early_serial_map; +#endif /* CONFIG_KGDB */ + +#endif + + return; +} diff --git a/arch/ppc/syslib/mv64360_pic.c b/arch/ppc/syslib/mv64360_pic.c new file mode 100644 index 000000000..ef0762955 --- /dev/null +++ b/arch/ppc/syslib/mv64360_pic.c @@ -0,0 +1,404 @@ +/* + * arch/ppc/kernel/mv64360_pic.c + * + * Interrupt controller support for Marvell's MV64360. + * + * Author: Rabeeh Khoury + * Based on MV64360 PIC written by + * Chris Zankel + * Mark A. Greer + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 or -2 (bogus interrupt) + * + */ +int +mv64360_get_irq(struct pt_regs *regs) +{ + int irq; + int irq_gpp; + +#ifdef CONFIG_SMP +#define BIT28 (1<<28) + /* + * Second CPU gets only doorbell (message) interrupts. + * The doorbell interrupt is BIT28 in the main interrupt low cause reg. + */ + int cpu_nr = smp_processor_id(); + if (cpu_nr == 1) { + irq = mv64x60_read(&base_bh, MV64360_IC_MAIN_CAUSE_LO); + if (!(irq & BIT28)) + return -1; + return 28; + } +#endif + + irq = mv64x60_read(&base_bh, MV64360_IC_MAIN_CAUSE_LO); + irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]); + if (irq == -1) { + irq = mv64x60_read(&base_bh, MV64360_IC_MAIN_CAUSE_HI); + irq = __ilog2((irq & 0x1f0003f7) & ppc_cached_irq_mask[1]); + if (irq == -1) { + irq = -2; /* bogus interrupt, should never happen */ + } else { + if ((irq >= 24) && (irq < 28)) { + irq_gpp = + mv64x60_read(&base_bh, + MV64x60_GPP_INTR_CAUSE); + irq_gpp = + __ilog2(irq_gpp & + ppc_cached_irq_mask[2]); + + if (irq_gpp == -1) { + irq = -2; + } else { + irq = irq_gpp + 64; + mv64x60_write(&base_bh, + MV64x60_GPP_INTR_CAUSE, + ~(1 << (irq - 64))); + } + } else { + irq += 32; + } + } + } + + if (irq < 0) { + return (irq); + } else { + return (mv64360_irq_base + irq); + } +} + +/* mv64360_unmask_irq() + * + * This function enables an interrupt. + * + * Input Variable(s): + * unsigned int interrupt number (IRQ0...IRQ95). + * + * Output Variable(s): + * None. + * + * Returns: + * void + */ + +static void +mv64360_unmask_irq(unsigned int irq) +{ +#ifdef CONFIG_SMP + /* second CPU gets only doorbell interrupts */ + if ((irq - mv64360_irq_base) == 28) { + mv64x60_set_bits(&base_bh, MV64360_IC_CPU1_INTR_MASK_LO, BIT28); + return; + } +#endif + irq -= mv64360_irq_base; + if (irq > 31) { + if (irq > 63) { + /* unmask GPP irq */ + mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK, + ppc_cached_irq_mask[2] |= (1 << (irq - 64))); + } else { + /* mask high interrupt register */ + mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_HI, + ppc_cached_irq_mask[1] |= (1 << (irq - 32))); + } + } else { + /* mask low interrupt register */ + mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_LO, + ppc_cached_irq_mask[0] |= (1 << irq)); + } +} + + +/* mv64360_mask_irq() + * + * This function disables the requested interrupt. + * + * Input Variable(s): + * unsigned int interrupt number (IRQ0...IRQ95). + * + * Output Variable(s): + * None. + * + * Returns: + * void + */ + +static void +mv64360_mask_irq(unsigned int irq) +{ +#ifdef CONFIG_SMP + if ((irq - mv64360_irq_base) == 28) { + mv64x60_clr_bits(&base_bh, MV64360_IC_CPU1_INTR_MASK_LO, BIT28); + return; + } +#endif + irq -= mv64360_irq_base; + if (irq > 31) { + if (irq > 63) { + /* mask GPP irq */ + mv64x60_write(&base_bh, MV64x60_GPP_INTR_MASK, + ppc_cached_irq_mask[2] &= ~(1 << (irq - 64))); + } else { + /* mask high interrupt register */ + mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_HI, + ppc_cached_irq_mask[1] &= ~(1 << (irq - 32))); + } + } else { + /* mask low interrupt register */ + mv64x60_write(&base_bh, MV64360_IC_CPU0_INTR_MASK_LO, + ppc_cached_irq_mask[0] &= ~(1 << irq)); + } + +} + +static irqreturn_t +mv64360_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 val; + val = mv64x60_read(&base_bh, MV64x60_CPU_ERR_CAUSE); + printk(KERN_ERR + "mv64360_cpu_error_int_handler: Error on CPU interface - Cause regiser 0x%08x\n", + val); + printk(KERN_ERR "\tCPU error register dump:\n"); + printk(KERN_ERR "\tAddress low 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_CPU_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress high 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_CPU_ERR_ADDR_HI)); + printk(KERN_ERR "\tData low 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_CPU_ERR_DATA_LO)); + printk(KERN_ERR "\tData high 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_CPU_ERR_DATA_HI)); + printk(KERN_ERR "\tParity 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_CPU_ERR_PARITY)); + mv64x60_write(&base_bh, MV64x60_CPU_ERR_CAUSE, 0); + return IRQ_HANDLED; +} + +static irqreturn_t +mv64360_sram_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + printk(KERN_ERR + "mv64360_sram_error_int_handler: Error in internal SRAM - Cause register 0x%08x\n", + mv64x60_read(&base_bh, MV64360_SRAM_ERR_CAUSE)); + printk(KERN_ERR "\tSRAM error register dump:\n"); + printk(KERN_ERR "\tAddress Low 0x%08x\n", + mv64x60_read(&base_bh, MV64360_SRAM_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress High 0x%08x\n", + mv64x60_read(&base_bh, MV64360_SRAM_ERR_ADDR_HI)); + printk(KERN_ERR "\tData Low 0x%08x\n", + mv64x60_read(&base_bh, MV64360_SRAM_ERR_DATA_LO)); + printk(KERN_ERR "\tData High 0x%08x\n", + mv64x60_read(&base_bh, MV64360_SRAM_ERR_DATA_HI)); + printk(KERN_ERR "\tParity 0x%08x\n", + mv64x60_read(&base_bh, MV64360_SRAM_ERR_PARITY)); + mv64x60_write(&base_bh, MV64360_SRAM_ERR_CAUSE, 0); + return IRQ_HANDLED; +} + +static irqreturn_t +mv64360_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 val; + unsigned int pci_bus = (unsigned int) dev_id; + if (pci_bus == 0) { /* Error on PCI 0 */ + val = mv64x60_read(&base_bh, MV64x60_PCI0_ERR_CAUSE); + printk(KERN_ERR + "mv64360_pci_error_int_handler: Error in PCI %d Interface\n", + pci_bus); + printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); + printk(KERN_ERR "\tCause register 0x%08x\n", val); + printk(KERN_ERR "\tAddress Low 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_PCI0_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress High 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_PCI0_ERR_ADDR_HI)); + printk(KERN_ERR "\tAttribute 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_PCI0_ERR_DATA_LO)); + printk(KERN_ERR "\tCommand 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_PCI0_ERR_CMD)); + mv64x60_write(&base_bh, MV64x60_PCI0_ERR_CAUSE, ~val); + } + if (pci_bus == 1) { /* Error on PCI 1 */ + val = mv64x60_read(&base_bh, MV64x60_PCI1_ERR_CAUSE); + printk(KERN_ERR + "mv64360_pci_error_int_handler: Error in PCI %d Interface\n", + pci_bus); + printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); + printk(KERN_ERR "\tCause register 0x%08x\n", val); + printk(KERN_ERR "\tAddress Low 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_PCI1_ERR_ADDR_LO)); + printk(KERN_ERR "\tAddress High 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_PCI1_ERR_ADDR_HI)); + printk(KERN_ERR "\tAttribute 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_PCI1_ERR_DATA_LO)); + printk(KERN_ERR "\tCommand 0x%08x\n", + mv64x60_read(&base_bh, MV64x60_PCI1_ERR_CMD)); + mv64x60_write(&base_bh, MV64x60_PCI1_ERR_CAUSE, ~val); + } + return IRQ_HANDLED; +} diff --git a/arch/ppc/syslib/mv64x60.c b/arch/ppc/syslib/mv64x60.c new file mode 100644 index 000000000..c31fe5330 --- /dev/null +++ b/arch/ppc/syslib/mv64x60.c @@ -0,0 +1,2872 @@ +/* + * arch/ppc/syslib/mv64x60.c + * + * Common routines for the Marvell/Galileo Discovery line of host bridges + * (e.g, gt64260 and mv64360). + * + * Author: Mark A. Greer + * Rabeeh Khoury + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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; ici->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; iSystem 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; ici->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; ici->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; iwindow_preserve_mask_32 & (1<window_preserve_mask_64 & (1<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-> 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; iwindow_preserve_mask_32 & (1<window_preserve_mask_64 & (1<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-> windows */ + mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x0000f9ff); + mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x0000f9ff); + + return; +} + +/* + * mv64360_chip_specific_init() + * + * No errata work arounds for the MV64360 implemented at this point. + */ +static void +mv64360_chip_specific_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si) +{ + struct ocp_device *dev; + mv64x60_ocp_mpsc_data_t *mpsc_dp; + + if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 0)) + != NULL) { + mpsc_dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions; + mpsc_dp->brg_can_tune = 1; + } + + if ((dev = ocp_find_device(OCP_VENDOR_MARVELL, OCP_FUNC_MPSC, 1)) + != NULL) { + mpsc_dp = (mv64x60_ocp_mpsc_data_t *)dev->def->additions; + mpsc_dp->brg_can_tune = 1; + } + + return; +} + +/* + * mv64460_chip_specific_init() + * + * No errata work arounds for the MV64460 implemented at this point. + */ +static void +mv64460_chip_specific_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si) +{ + mv64360_chip_specific_init(bh, si); /* XXXX check errata */ + return; +} diff --git a/arch/ppc/syslib/mv64x60_ocp.c b/arch/ppc/syslib/mv64x60_ocp.c new file mode 100644 index 000000000..935bb9a4a --- /dev/null +++ b/arch/ppc/syslib/mv64x60_ocp.c @@ -0,0 +1,133 @@ +/* + * arch/ppc/syslib/mv64x60_ocp.c + * + * Common OCP definitions for the Marvell GT64260/MV64360/MV64460/... + * line of host bridges. + * + * Author: Mark A. Greer + * + * 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 +#include +#include +#include + +static mv64x60_ocp_mpsc_data_t mv64x60_ocp_mpsc0_def = { + .mirror_regs = 0, + .cache_mgmt = 0, + .max_idle = 0, + .default_baud = 9600, + .default_bits = 8, + .default_parity = 'n', + .default_flow = 'n', + .chr_1_val = 0x00000000, + .chr_2_val = 0x00000000, + .chr_10_val = 0x00000003, + .mpcr_val = 0, + .mrr_val = 0x3ffffe38, + .rcrr_val = 0, + .tcrr_val = 0, + .intr_mask_val = 0, + .bcr_val = 0, + .sdma_irq = MV64x60_IRQ_SDMA_0, + .brg_can_tune = 0, + .brg_clk_src = 8, /* Default to TCLK */ + .brg_clk_freq = 100000000, /* Default to 100 MHz */ +}; +static mv64x60_ocp_mpsc_data_t mv64x60_ocp_mpsc1_def = { + .mirror_regs = 0, + .cache_mgmt = 0, + .max_idle = 0, + .default_baud = 9600, + .default_bits = 8, + .default_parity = 'n', + .default_flow = 'n', + .chr_1_val = 0x00000000, + .chr_1_val = 0x00000000, + .chr_2_val = 0x00000000, + .chr_10_val = 0x00000003, + .mpcr_val = 0, + .mrr_val = 0x3ffffe38, + .rcrr_val = 0, + .tcrr_val = 0, + .intr_mask_val = 0, + .bcr_val = 0, + .sdma_irq = MV64x60_IRQ_SDMA_1, + .brg_can_tune = 0, + .brg_clk_src = 8, /* Default to TCLK */ + .brg_clk_freq = 100000000, /* Default to 100 MHz */ +}; +MV64x60_OCP_SYSFS_MPSC_DATA() + +struct ocp_def core_ocp[] = { + /* Base address for the block of bridge's regs */ + { .vendor = OCP_VENDOR_MARVELL, /* 0x00 */ + .function = OCP_FUNC_HB, + .index = 0, + .paddr = 0, + .pm = OCP_CPM_NA, + }, + /* 10/100 Ethernet controller */ + { .vendor = OCP_VENDOR_MARVELL, /* 0x01 */ + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = GT64260_ENET_0_OFFSET, + .irq = MV64x60_IRQ_ETH_0, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_MARVELL, /* 0x02 */ + .function = OCP_FUNC_EMAC, + .index = 1, + .paddr = GT64260_ENET_1_OFFSET, + .irq = MV64x60_IRQ_ETH_1, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_MARVELL, /* 0x03 */ + .function = OCP_FUNC_EMAC, + .index = 2, + .paddr = GT64260_ENET_2_OFFSET, + .irq = MV64x60_IRQ_ETH_2, + .pm = OCP_CPM_NA, + }, + /* Multi-Protocol Serial Controller (MPSC) */ + { .vendor = OCP_VENDOR_MARVELL, /* 0x04 */ + .function = OCP_FUNC_MPSC, + .index = 0, + .paddr = MV64x60_MPSC_0_OFFSET, + .irq = MV64x60_IRQ_MPSC_0, + .pm = OCP_CPM_NA, + .additions = &mv64x60_ocp_mpsc0_def, + .show = &mv64x60_ocp_show_mpsc + }, + { .vendor = OCP_VENDOR_MARVELL, /* 0x05 */ + .function = OCP_FUNC_MPSC, + .index = 1, + .paddr = MV64x60_MPSC_1_OFFSET, + .irq = MV64x60_IRQ_MPSC_1, + .pm = OCP_CPM_NA, + .additions = &mv64x60_ocp_mpsc1_def, + .show = &mv64x60_ocp_show_mpsc + }, + /* Inter-Integrated Circuit Controller */ + { .vendor = OCP_VENDOR_MARVELL, /* 0x06 */ + .function = OCP_FUNC_I2C, + .index = 0, + .paddr = GT64260_I2C_OFFSET, + .irq = MV64x60_IRQ_I2C, + .pm = OCP_CPM_NA, + }, + /* Programmable Interrupt Controller */ + { .vendor = OCP_VENDOR_MARVELL, /* 0x07 */ + .function = OCP_FUNC_PIC, + .index = 0, + .paddr = GT64260_IC_OFFSET, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c new file mode 100644 index 000000000..d1a785628 --- /dev/null +++ b/drivers/mtd/chips/cfi_util.c @@ -0,0 +1,92 @@ +/* + * Common Flash Interface support: + * Generic utility functions not dependant on command set + * + * Copyright (C) 2002 Red Hat + * Copyright (C) 2003 STMicroelectronics Limited + * + * This code is covered by the GPL. + * + * $Id: cfi_util.c,v 1.4 2004/07/14 08:38:44 dwmw2 Exp $ + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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; iMajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { + printk(KERN_WARNING " Unknown %s Extended Query " + "version %c.%c.\n", name, extp->MajorVersion, + extp->MinorVersion); + kfree(extp); + extp = NULL; + goto out; + } + +out: + /* Make sure it's in read mode */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); + + return extp; +} + +EXPORT_SYMBOL(cfi_read_pri); + +void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_fixup *f; + + for (f=fixups; f->fixup; f++) { + if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && + ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { + f->fixup(map, f->param); + } + } +} + +EXPORT_SYMBOL(cfi_fixup); + +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c new file mode 100644 index 000000000..5f66e9bfb --- /dev/null +++ b/drivers/mtd/devices/phram.c @@ -0,0 +1,362 @@ +/** + * + * $Id: phram.c,v 1.1 2003/08/21 17:52:30 joern Exp $ + * + * Copyright (c) Jochen Schaeuble + * 07/2003 rewritten by Joern Engel + * + * 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=,, + * may be up to 63 characters. + * and can be octal, decimal or hexadecimal. If followed + * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or + * gigabytes. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#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=,\""); + +/* + * 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=,\""); + + +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 "); +MODULE_DESCRIPTION("MTD driver for physical RAM"); diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c new file mode 100644 index 000000000..ec2612b0d --- /dev/null +++ b/drivers/mtd/maps/ichxrom.c @@ -0,0 +1,407 @@ +/* + * ichxrom.c + * + * Normal mappings of chips in physical memory + * $Id: ichxrom.c,v 1.7 2004/07/14 18:14:09 eric Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge"); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c new file mode 100644 index 000000000..677c21685 --- /dev/null +++ b/drivers/mtd/nand/diskonchip.c @@ -0,0 +1,1237 @@ +/* + * drivers/mtd/nand/diskonchip.c + * + * (C) 2003 Red Hat, Inc. + * + * Author: David Woodhouse + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* 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 "); +MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c new file mode 100644 index 000000000..596bc8f70 --- /dev/null +++ b/drivers/mtd/nand/nand_base.c @@ -0,0 +1,2581 @@ +/* + * drivers/mtd/nand.c + * + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * Basic support for AG-AND chips is provided. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/tech/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002 Thomas Gleixner (tglx@linutronix.de) + * + * 02-08-2004 tglx: support for strange chips, which cannot auto increment + * pages on read / read_oob + * + * 03-17-2004 tglx: Check ready before auto increment check. Simon Bayes + * pointed this out, as he marked an auto increment capable chip + * as NOAUTOINCR in the board driver. + * Make reads over block boundaries work too + * + * 04-14-2004 tglx: first working version for 2k page size chips + * + * 05-19-2004 tglx: Basic support for Renesas AG-AND chips + * + * Credits: + * David Woodhouse for adding multichip support + * + * Aleph One Ltd. and Toby Churchill Ltd. for supporting the + * rework for 2K page size chips + * + * TODO: + * Enable cached programming for 2k page size chips + * Check, if mtd->ecctype should be set to MTD_ECC_HW + * if we have HW ecc support. + * The AG-AND chips have nice features for speed improvement, + * which are not supported yet. Read / program 4 pages in one go. + * + * $Id: nand_base.c,v 1.113 2004/07/14 16:31:31 gleixner Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) +#include +#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; iIO_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; iIO_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; iIO_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; iIO_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; iIO_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; iIO_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 , Thomas Gleixner "); +MODULE_DESCRIPTION ("Generic NAND flash driver code"); diff --git a/drivers/mtd/nand/tx4925ndfmc.c b/drivers/mtd/nand/tx4925ndfmc.c new file mode 100644 index 000000000..94a616561 --- /dev/null +++ b/drivers/mtd/nand/tx4925ndfmc.c @@ -0,0 +1,442 @@ +/* + * drivers/mtd/tx4925ndfmc.c + * + * Overview: + * This is a device driver for the NAND flash device found on the + * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports + * 16MiB, 32MiB and 64MiB cards. + * + * Author: MontaVista Software, Inc. source@mvista.com + * + * Derived from drivers/mtd/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * $Id: tx4925ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $ + * + * Copyright (C) 2001 Toshiba Corporation + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; iIO_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; iIO_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; iIO_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 "); +MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925"); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c new file mode 100644 index 000000000..5005c1c94 --- /dev/null +++ b/drivers/net/gianfar.c @@ -0,0 +1,1926 @@ +/* + * drivers/net/gianfar.c + * + * Gianfar Ethernet Driver + * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560 + * Based on 8260_io/fcc_enet.c + * + * Author: Andy Fleming + * Maintainer: Kumar Gala (kumar.gala@freescale.com) + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Gianfar: AKA Lambda Draconis, "Dragon" + * RA 11 31 24.2 + * Dec +69 19 52 + * V 3.84 + * B-V +1.62 + * + * Theory of operation + * This driver is designed for the Triple-speed Ethernet + * controllers on the Freescale 8540/8560 integrated processors, + * as well as the Fast Ethernet Controller on the 8540. + * + * The driver is initialized through OCP. Structures which + * define the configuration needed by the board are defined in a + * board structure in arch/ppc/platforms (though I do not + * discount the possibility that other architectures could one + * day be supported. One assumption the driver currently makes + * is that the PHY is configured in such a way to advertise all + * capabilities. This is a sensible default, and on certain + * PHYs, changing this default encounters substantial errata + * issues. Future versions may remove this requirement, but for + * now, it is best for the firmware to ensure this is the case. + * + * The Gianfar Ethernet Controller uses a ring of buffer + * descriptors. The beginning is indicated by a register + * pointing to the physical address of the start of the ring. + * The end is determined by a "wrap" bit being set in the + * last descriptor of the ring. + * + * When a packet is received, the RXF bit in the + * IEVENT register is set, triggering an interrupt when the + * corresponding bit in the IMASK register is also set (if + * interrupt coalescing is active, then the interrupt may not + * happen immediately, but will wait until either a set number + * of frames or amount of time have passed.). In NAPI, the + * interrupt handler will signal there is work to be done, and + * exit. Without NAPI, the packet(s) will be handled + * immediately. Both methods will start at the last known empty + * descriptor, and process every subsequent descriptor until there + * are none left with data (NAPI will stop after a set number of + * packets to give time to other tasks, but will eventually + * process all the packets). The data arrives inside a + * pre-allocated skb, and so after the skb is passed up to the + * stack, a new skb must be allocated, and the address field in + * the buffer descriptor must be updated to indicate this new + * skb. + * + * When the kernel requests that a packet be transmitted, the + * driver starts where it left off last time, and points the + * descriptor at the buffer which was passed in. The driver + * then informs the DMA engine that there are packets ready to + * be transmitted. Once the controller is finished transmitting + * the packet, an interrupt may be triggered (under the same + * conditions as for reception, but depending on the TXF bit). + * The driver then cleans up the buffer. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gianfar.h" +#include "gianfar_phy.h" +#ifdef CONFIG_NET_FASTROUTE +#include +#include +#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); diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h new file mode 100644 index 000000000..f7af3465c --- /dev/null +++ b/drivers/net/gianfar.h @@ -0,0 +1,537 @@ +/* + * drivers/net/gianfar.h + * + * Gianfar Ethernet Driver + * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560 + * Based on 8260_io/fcc_enet.c + * + * Author: Andy Fleming + * Maintainer: Kumar Gala (kumar.gala@freescale.com) + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Still left to do: + * -Add support for module parameters + */ +#ifndef __GIANFAR_H +#define __GIANFAR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41) +#include +#else +#include +#define work_struct tq_struct +#define schedule_work schedule_task +#endif + +#include +#include +#include +#include "gianfar_phy.h" + +/* The maximum number of packets to be handled in one call of gfar_poll */ +#define GFAR_DEV_WEIGHT 64 + +/* Number of bytes to align the rx bufs to */ +#define RXBUF_ALIGNMENT 64 + +/* The number of bytes which composes a unit for the purpose of + * allocating data buffers. ie-for any given MTU, the data buffer + * will be the next highest multiple of 512 bytes. */ +#define INCREMENTAL_BUFFER_SIZE 512 + + +#define MAC_ADDR_LEN 6 + +extern char gfar_driver_name[]; +extern char gfar_driver_version[]; + +/* These need to be powers of 2 for this driver */ +#ifdef CONFIG_GFAR_NAPI +#define DEFAULT_TX_RING_SIZE 256 +#define DEFAULT_RX_RING_SIZE 256 +#else +#define DEFAULT_TX_RING_SIZE 64 +#define DEFAULT_RX_RING_SIZE 64 +#endif + +#define GFAR_RX_MAX_RING_SIZE 256 +#define GFAR_TX_MAX_RING_SIZE 256 + +#define DEFAULT_RX_BUFFER_SIZE 1536 +#define TX_RING_MOD_MASK(size) (size-1) +#define RX_RING_MOD_MASK(size) (size-1) +#define JUMBO_BUFFER_SIZE 9728 +#define JUMBO_FRAME_SIZE 9600 + +/* Latency of interface clock in nanoseconds */ +/* Interface clock latency , in this case, means the + * time described by a value of 1 in the interrupt + * coalescing registers' time fields. Since those fields + * refer to the time it takes for 64 clocks to pass, the + * latencies are as such: + * GBIT = 125MHz => 8ns/clock => 8*64 ns / tick + * 100 = 25 MHz => 40ns/clock => 40*64 ns / tick + * 10 = 2.5 MHz => 400ns/clock => 400*64 ns / tick + */ +#define GFAR_GBIT_TIME 512 +#define GFAR_100_TIME 2560 +#define GFAR_10_TIME 25600 + +#define DEFAULT_TXCOUNT 16 +#define DEFAULT_TXTIME 32768 + +#define DEFAULT_RXCOUNT 16 +#define DEFAULT_RXTIME 32768 + +#define TBIPA_VALUE 0x1f +#define MIIMCFG_INIT_VALUE 0x00000007 +#define MIIMCFG_RESET 0x80000000 +#define MIIMIND_BUSY 0x00000001 + +/* MAC register bits */ +#define MACCFG1_SOFT_RESET 0x80000000 +#define MACCFG1_RESET_RX_MC 0x00080000 +#define MACCFG1_RESET_TX_MC 0x00040000 +#define MACCFG1_RESET_RX_FUN 0x00020000 +#define MACCFG1_RESET_TX_FUN 0x00010000 +#define MACCFG1_LOOPBACK 0x00000100 +#define MACCFG1_RX_FLOW 0x00000020 +#define MACCFG1_TX_FLOW 0x00000010 +#define MACCFG1_SYNCD_RX_EN 0x00000008 +#define MACCFG1_RX_EN 0x00000004 +#define MACCFG1_SYNCD_TX_EN 0x00000002 +#define MACCFG1_TX_EN 0x00000001 + +#define MACCFG2_INIT_SETTINGS 0x00007205 +#define MACCFG2_FULL_DUPLEX 0x00000001 +#define MACCFG2_IF 0x00000300 +#define MACCFG2_MII 0x00000100 +#define MACCFG2_GMII 0x00000200 +#define MACCFG2_HUGEFRAME 0x00000020 +#define MACCFG2_LENGTHCHECK 0x00000010 + +#define ECNTRL_INIT_SETTINGS 0x00001000 +#define ECNTRL_TBI_MODE 0x00000020 + +#define MRBLR_INIT_SETTINGS DEFAULT_RX_BUFFER_SIZE + +#define MINFLR_INIT_SETTINGS 0x00000040 + +/* Init to do tx snooping for buffers and descriptors */ +#define DMACTRL_INIT_SETTINGS 0x000000c3 +#define DMACTRL_GRS 0x00000010 +#define DMACTRL_GTS 0x00000008 + +#define TSTAT_CLEAR_THALT 0x80000000 + +/* Interrupt coalescing macros */ +#define IC_ICEN 0x80000000 +#define IC_ICFT_MASK 0x1fe00000 +#define IC_ICFT_SHIFT 21 +#define mk_ic_icft(x) \ + (((unsigned int)x << IC_ICFT_SHIFT)&IC_ICFT_MASK) +#define IC_ICTT_MASK 0x0000ffff +#define mk_ic_ictt(x) (x&IC_ICTT_MASK) + +#define mk_ic_value(count, time) (IC_ICEN | \ + mk_ic_icft(count) | \ + mk_ic_ictt(time)) + +#define RCTRL_PROM 0x00000008 +#define RSTAT_CLEAR_RHALT 0x00800000 + +#define IEVENT_INIT_CLEAR 0xffffffff +#define IEVENT_BABR 0x80000000 +#define IEVENT_RXC 0x40000000 +#define IEVENT_BSY 0x20000000 +#define IEVENT_EBERR 0x10000000 +#define IEVENT_MSRO 0x04000000 +#define IEVENT_GTSC 0x02000000 +#define IEVENT_BABT 0x01000000 +#define IEVENT_TXC 0x00800000 +#define IEVENT_TXE 0x00400000 +#define IEVENT_TXB 0x00200000 +#define IEVENT_TXF 0x00100000 +#define IEVENT_LC 0x00040000 +#define IEVENT_CRL 0x00020000 +#define IEVENT_XFUN 0x00010000 +#define IEVENT_RXB0 0x00008000 +#define IEVENT_GRSC 0x00000100 +#define IEVENT_RXF0 0x00000080 +#define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0) +#define IEVENT_TX_MASK (IEVENT_TXB | IEVENT_TXF) +#define IEVENT_ERR_MASK \ +(IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ + IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ + | IEVENT_CRL | IEVENT_XFUN) + +#define IMASK_INIT_CLEAR 0x00000000 +#define IMASK_BABR 0x80000000 +#define IMASK_RXC 0x40000000 +#define IMASK_BSY 0x20000000 +#define IMASK_EBERR 0x10000000 +#define IMASK_MSRO 0x04000000 +#define IMASK_GRSC 0x02000000 +#define IMASK_BABT 0x01000000 +#define IMASK_TXC 0x00800000 +#define IMASK_TXEEN 0x00400000 +#define IMASK_TXBEN 0x00200000 +#define IMASK_TXFEN 0x00100000 +#define IMASK_LC 0x00040000 +#define IMASK_CRL 0x00020000 +#define IMASK_XFUN 0x00010000 +#define IMASK_RXB0 0x00008000 +#define IMASK_GTSC 0x00000100 +#define IMASK_RXFEN0 0x00000080 +#define IMASK_RX_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY) +#define IMASK_DEFAULT (IMASK_TXEEN | IMASK_TXFEN | IMASK_TXBEN | \ + IMASK_RXFEN0 | IMASK_BSY | IMASK_EBERR | IMASK_BABR | \ + IMASK_XFUN | IMASK_RXC | IMASK_BABT) + + +/* Attribute fields */ + +/* This enables rx snooping for buffers and descriptors */ +#ifdef CONFIG_GFAR_BDSTASH +#define ATTR_BDSTASH 0x00000800 +#else +#define ATTR_BDSTASH 0x00000000 +#endif + +#ifdef CONFIG_GFAR_BUFSTASH +#define ATTR_BUFSTASH 0x00004000 +#define STASH_LENGTH 64 +#else +#define ATTR_BUFSTASH 0x00000000 +#endif + +#define ATTR_SNOOPING 0x000000c0 +#define ATTR_INIT_SETTINGS (ATTR_SNOOPING \ + | ATTR_BDSTASH | ATTR_BUFSTASH) + +#define ATTRELI_INIT_SETTINGS 0x0 + + +/* TxBD status field bits */ +#define TXBD_READY 0x8000 +#define TXBD_PADCRC 0x4000 +#define TXBD_WRAP 0x2000 +#define TXBD_INTERRUPT 0x1000 +#define TXBD_LAST 0x0800 +#define TXBD_CRC 0x0400 +#define TXBD_DEF 0x0200 +#define TXBD_HUGEFRAME 0x0080 +#define TXBD_LATECOLLISION 0x0080 +#define TXBD_RETRYLIMIT 0x0040 +#define TXBD_RETRYCOUNTMASK 0x003c +#define TXBD_UNDERRUN 0x0002 + +/* RxBD status field bits */ +#define RXBD_EMPTY 0x8000 +#define RXBD_RO1 0x4000 +#define RXBD_WRAP 0x2000 +#define RXBD_INTERRUPT 0x1000 +#define RXBD_LAST 0x0800 +#define RXBD_FIRST 0x0400 +#define RXBD_MISS 0x0100 +#define RXBD_BROADCAST 0x0080 +#define RXBD_MULTICAST 0x0040 +#define RXBD_LARGE 0x0020 +#define RXBD_NONOCTET 0x0010 +#define RXBD_SHORT 0x0008 +#define RXBD_CRCERR 0x0004 +#define RXBD_OVERRUN 0x0002 +#define RXBD_TRUNCATED 0x0001 +#define RXBD_STATS 0x01ff + +struct txbd8 +{ + u16 status; /* Status Fields */ + u16 length; /* Buffer length */ + u32 bufPtr; /* Buffer Pointer */ +}; + +struct rxbd8 +{ + u16 status; /* Status Fields */ + u16 length; /* Buffer Length */ + u32 bufPtr; /* Buffer Pointer */ +}; + +struct rmon_mib +{ + u32 tr64; /* 0x.680 - Transmit and Receive 64-byte Frame Counter */ + u32 tr127; /* 0x.684 - Transmit and Receive 65-127 byte Frame Counter */ + u32 tr255; /* 0x.688 - Transmit and Receive 128-255 byte Frame Counter */ + u32 tr511; /* 0x.68c - Transmit and Receive 256-511 byte Frame Counter */ + u32 tr1k; /* 0x.690 - Transmit and Receive 512-1023 byte Frame Counter */ + u32 trmax; /* 0x.694 - Transmit and Receive 1024-1518 byte Frame Counter */ + u32 trmgv; /* 0x.698 - Transmit and Receive 1519-1522 byte Good VLAN Frame */ + u32 rbyt; /* 0x.69c - Receive Byte Counter */ + u32 rpkt; /* 0x.6a0 - Receive Packet Counter */ + u32 rfcs; /* 0x.6a4 - Receive FCS Error Counter */ + u32 rmca; /* 0x.6a8 - Receive Multicast Packet Counter */ + u32 rbca; /* 0x.6ac - Receive Broadcast Packet Counter */ + u32 rxcf; /* 0x.6b0 - Receive Control Frame Packet Counter */ + u32 rxpf; /* 0x.6b4 - Receive Pause Frame Packet Counter */ + u32 rxuo; /* 0x.6b8 - Receive Unknown OP Code Counter */ + u32 raln; /* 0x.6bc - Receive Alignment Error Counter */ + u32 rflr; /* 0x.6c0 - Receive Frame Length Error Counter */ + u32 rcde; /* 0x.6c4 - Receive Code Error Counter */ + u32 rcse; /* 0x.6c8 - Receive Carrier Sense Error Counter */ + u32 rund; /* 0x.6cc - Receive Undersize Packet Counter */ + u32 rovr; /* 0x.6d0 - Receive Oversize Packet Counter */ + u32 rfrg; /* 0x.6d4 - Receive Fragments Counter */ + u32 rjbr; /* 0x.6d8 - Receive Jabber Counter */ + u32 rdrp; /* 0x.6dc - Receive Drop Counter */ + u32 tbyt; /* 0x.6e0 - Transmit Byte Counter Counter */ + u32 tpkt; /* 0x.6e4 - Transmit Packet Counter */ + u32 tmca; /* 0x.6e8 - Transmit Multicast Packet Counter */ + u32 tbca; /* 0x.6ec - Transmit Broadcast Packet Counter */ + u32 txpf; /* 0x.6f0 - Transmit Pause Control Frame Counter */ + u32 tdfr; /* 0x.6f4 - Transmit Deferral Packet Counter */ + u32 tedf; /* 0x.6f8 - Transmit Excessive Deferral Packet Counter */ + u32 tscl; /* 0x.6fc - Transmit Single Collision Packet Counter */ + u32 tmcl; /* 0x.700 - Transmit Multiple Collision Packet Counter */ + u32 tlcl; /* 0x.704 - Transmit Late Collision Packet Counter */ + u32 txcl; /* 0x.708 - Transmit Excessive Collision Packet Counter */ + u32 tncl; /* 0x.70c - Transmit Total Collision Counter */ + u8 res1[4]; + u32 tdrp; /* 0x.714 - Transmit Drop Frame Counter */ + u32 tjbr; /* 0x.718 - Transmit Jabber Frame Counter */ + u32 tfcs; /* 0x.71c - Transmit FCS Error Counter */ + u32 txcf; /* 0x.720 - Transmit Control Frame Counter */ + u32 tovr; /* 0x.724 - Transmit Oversize Frame Counter */ + u32 tund; /* 0x.728 - Transmit Undersize Frame Counter */ + u32 tfrg; /* 0x.72c - Transmit Fragments Frame Counter */ + u32 car1; /* 0x.730 - Carry Register One */ + u32 car2; /* 0x.734 - Carry Register Two */ + u32 cam1; /* 0x.738 - Carry Mask Register One */ + u32 cam2; /* 0x.73c - Carry Mask Register Two */ +}; + +struct gfar_extra_stats { + u64 kernel_dropped; + u64 rx_large; + u64 rx_short; + u64 rx_nonoctet; + u64 rx_crcerr; + u64 rx_overrun; + u64 rx_bsy; + u64 rx_babr; + u64 rx_trunc; + u64 eberr; + u64 tx_babt; + u64 tx_underrun; + u64 rx_skbmissing; + u64 tx_timeout; +}; + +#define GFAR_RMON_LEN ((sizeof(struct rmon_mib) - 16)/sizeof(u32)) +#define GFAR_EXTRA_STATS_LEN (sizeof(struct gfar_extra_stats)/sizeof(u64)) + +/* Number of stats in the stats structure (ignore car and cam regs)*/ +#define GFAR_STATS_LEN (GFAR_RMON_LEN + GFAR_EXTRA_STATS_LEN) + +#define GFAR_INFOSTR_LEN 32 + +struct gfar_stats { + u64 extra[GFAR_EXTRA_STATS_LEN]; + u64 rmon[GFAR_RMON_LEN]; +}; + + +struct gfar { + u8 res1[16]; + u32 ievent; /* 0x.010 - Interrupt Event Register */ + u32 imask; /* 0x.014 - Interrupt Mask Register */ + u32 edis; /* 0x.018 - Error Disabled Register */ + u8 res2[4]; + u32 ecntrl; /* 0x.020 - Ethernet Control Register */ + u32 minflr; /* 0x.024 - Minimum Frame Length Register */ + u32 ptv; /* 0x.028 - Pause Time Value Register */ + u32 dmactrl; /* 0x.02c - DMA Control Register */ + u32 tbipa; /* 0x.030 - TBI PHY Address Register */ + u8 res3[88]; + u32 fifo_tx_thr; /* 0x.08c - FIFO transmit threshold register */ + u8 res4[8]; + u32 fifo_tx_starve; /* 0x.098 - FIFO transmit starve register */ + u32 fifo_tx_starve_shutoff; /* 0x.09c - FIFO transmit starve shutoff register */ + u8 res5[96]; + u32 tctrl; /* 0x.100 - Transmit Control Register */ + u32 tstat; /* 0x.104 - Transmit Status Register */ + u8 res6[4]; + u32 tbdlen; /* 0x.10c - Transmit Buffer Descriptor Data Length Register */ + u32 txic; /* 0x.110 - Transmit Interrupt Coalescing Configuration Register */ + u8 res7[16]; + u32 ctbptr; /* 0x.124 - Current Transmit Buffer Descriptor Pointer Register */ + u8 res8[92]; + u32 tbptr; /* 0x.184 - Transmit Buffer Descriptor Pointer Low Register */ + u8 res9[124]; + u32 tbase; /* 0x.204 - Transmit Descriptor Base Address Register */ + u8 res10[168]; + u32 ostbd; /* 0x.2b0 - Out-of-Sequence Transmit Buffer Descriptor Register */ + u32 ostbdp; /* 0x.2b4 - Out-of-Sequence Transmit Data Buffer Pointer Register */ + u8 res11[72]; + u32 rctrl; /* 0x.300 - Receive Control Register */ + u32 rstat; /* 0x.304 - Receive Status Register */ + u8 res12[4]; + u32 rbdlen; /* 0x.30c - RxBD Data Length Register */ + u32 rxic; /* 0x.310 - Receive Interrupt Coalescing Configuration Register */ + u8 res13[16]; + u32 crbptr; /* 0x.324 - Current Receive Buffer Descriptor Pointer */ + u8 res14[24]; + u32 mrblr; /* 0x.340 - Maximum Receive Buffer Length Register */ + u8 res15[64]; + u32 rbptr; /* 0x.384 - Receive Buffer Descriptor Pointer */ + u8 res16[124]; + u32 rbase; /* 0x.404 - Receive Descriptor Base Address */ + u8 res17[248]; + u32 maccfg1; /* 0x.500 - MAC Configuration 1 Register */ + u32 maccfg2; /* 0x.504 - MAC Configuration 2 Register */ + u32 ipgifg; /* 0x.508 - Inter Packet Gap/Inter Frame Gap Register */ + u32 hafdup; /* 0x.50c - Half Duplex Register */ + u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */ + u8 res18[12]; + u32 miimcfg; /* 0x.520 - MII Management Configuration Register */ + u32 miimcom; /* 0x.524 - MII Management Command Register */ + u32 miimadd; /* 0x.528 - MII Management Address Register */ + u32 miimcon; /* 0x.52c - MII Management Control Register */ + u32 miimstat; /* 0x.530 - MII Management Status Register */ + u32 miimind; /* 0x.534 - MII Management Indicator Register */ + u8 res19[4]; + u32 ifstat; /* 0x.53c - Interface Status Register */ + u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */ + u32 macstnaddr2; /* 0x.544 - Station Address Part 2 Register */ + u8 res20[312]; + struct rmon_mib rmon; + u8 res21[192]; + u32 iaddr0; /* 0x.800 - Indivdual address register 0 */ + u32 iaddr1; /* 0x.804 - Indivdual address register 1 */ + u32 iaddr2; /* 0x.808 - Indivdual address register 2 */ + u32 iaddr3; /* 0x.80c - Indivdual address register 3 */ + u32 iaddr4; /* 0x.810 - Indivdual address register 4 */ + u32 iaddr5; /* 0x.814 - Indivdual address register 5 */ + u32 iaddr6; /* 0x.818 - Indivdual address register 6 */ + u32 iaddr7; /* 0x.81c - Indivdual address register 7 */ + u8 res22[96]; + u32 gaddr0; /* 0x.880 - Global address register 0 */ + u32 gaddr1; /* 0x.884 - Global address register 1 */ + u32 gaddr2; /* 0x.888 - Global address register 2 */ + u32 gaddr3; /* 0x.88c - Global address register 3 */ + u32 gaddr4; /* 0x.890 - Global address register 4 */ + u32 gaddr5; /* 0x.894 - Global address register 5 */ + u32 gaddr6; /* 0x.898 - Global address register 6 */ + u32 gaddr7; /* 0x.89c - Global address register 7 */ + u8 res23[856]; + u32 attr; /* 0x.bf8 - Attributes Register */ + u32 attreli; /* 0x.bfc - Attributes Extract Length and Extract Index Register */ + u8 res24[1024]; + +}; + +/* Struct stolen almost completely (and shamelessly) from the FCC enet source + * (Ok, that's not so true anymore, but there is a family resemblence) + * The GFAR buffer descriptors track the ring buffers. The rx_bd_base + * and tx_bd_base always point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct gfar_private +{ + /* pointers to arrays of skbuffs for tx and rx */ + struct sk_buff ** tx_skbuff; + struct sk_buff ** rx_skbuff; + + /* indices pointing to the next free sbk in skb arrays */ + u16 skb_curtx; + u16 skb_currx; + + /* index of the first skb which hasn't been transmitted + * yet. */ + u16 skb_dirtytx; + + /* Configuration info for the coalescing features */ + unsigned char txcoalescing; + unsigned short txcount; + unsigned short txtime; + unsigned char rxcoalescing; + unsigned short rxcount; + unsigned short rxtime; + + /* GFAR addresses */ + struct rxbd8 *rx_bd_base; /* Base addresses of Rx and Tx Buffers */ + struct txbd8 *tx_bd_base; + struct rxbd8 *cur_rx; /* Next free rx ring entry */ + struct txbd8 *cur_tx; /* Next free ring entry */ + struct txbd8 *dirty_tx; /* The Ring entry to be freed. */ + struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */ + struct phy_info *phyinfo; + struct gfar *phyregs; + struct work_struct tq; + struct timer_list phy_info_timer; + struct net_device_stats stats; /* linux network statistics */ + struct gfar_extra_stats extra_stats; + spinlock_t lock; + unsigned int rx_buffer_size; + unsigned int rx_stash_size; + unsigned int tx_ring_size; + unsigned int rx_ring_size; + wait_queue_head_t rxcleanupq; + unsigned int rxclean; + int link; /* current link state */ + int oldlink; + int duplexity; /* Indicates negotiated duplex state */ + int olddplx; + int speed; /* Indicates negotiated speed */ + int oldspeed; + + /* Info structure initialized by board setup code */ + struct ocp_gfar_data *einfo; +}; + +extern inline u32 gfar_read(volatile unsigned *addr) +{ + u32 val; + val = in_be32(addr); + return val; +} + +extern inline void gfar_write(volatile unsigned *addr, u32 val) +{ + out_be32(addr, val); +} + + + +#endif /* __GIANFAR_H */ diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c new file mode 100644 index 000000000..4ccb5afd6 --- /dev/null +++ b/drivers/net/gianfar_ethtool.c @@ -0,0 +1,484 @@ +/* + * drivers/net/gianfar_ethtool.c + * + * Gianfar Ethernet Driver + * Ethtool support for Gianfar Enet + * Based on e1000 ethtool support + * + * Author: Andy Fleming + * Maintainer: Kumar Gala (kumar.gala@freescale.com) + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This software may be used and distributed according to + * the terms of the GNU Public License, Version 2, incorporated herein + * by reference. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gianfar.h" + +#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) + +extern int startup_gfar(struct net_device *dev); +extern void stop_gfar(struct net_device *dev); +extern void gfar_receive(int irq, void *dev_id, struct pt_regs *regs); + +void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, + u64 * buf); +void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf); +int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals); +int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals); +void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals); +int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals); +void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo); + +static char stat_gstrings[][ETH_GSTRING_LEN] = { + "RX Dropped by Kernel", + "RX Large Frame Errors", + "RX Short Frame Errors", + "RX Non-Octet Errors", + "RX CRC Errors", + "RX Overrun Errors", + "RX Busy Errors", + "RX Babbling Errors", + "RX Truncated Frames", + "Ethernet Bus Error", + "TX Babbling Errors", + "TX Underrun Errors", + "RX SKB Missing Errors", + "TX Timeout Errors", + "tx&rx 64B frames", + "tx&rx 65-127B frames", + "tx&rx 128-255B frames", + "tx&rx 256-511B frames", + "tx&rx 512-1023B frames", + "tx&rx 1024-1518B frames", + "tx&rx 1519-1522B Good VLAN", + "RX bytes", + "RX Packets", + "RX FCS Errors", + "Receive Multicast Packet", + "Receive Broadcast Packet", + "RX Control Frame Packets", + "RX Pause Frame Packets", + "RX Unknown OP Code", + "RX Alignment Error", + "RX Frame Length Error", + "RX Code Error", + "RX Carrier Sense Error", + "RX Undersize Packets", + "RX Oversize Packets", + "RX Fragmented Frames", + "RX Jabber Frames", + "RX Dropped Frames", + "TX Byte Counter", + "TX Packets", + "TX Multicast Packets", + "TX Broadcast Packets", + "TX Pause Control Frames", + "TX Deferral Packets", + "TX Excessive Deferral Packets", + "TX Single Collision Packets", + "TX Multiple Collision Packets", + "TX Late Collision Packets", + "TX Excessive Collision Packets", + "TX Total Collision", + "RESERVED", + "TX Dropped Frames", + "TX Jabber Frames", + "TX FCS Errors", + "TX Control Frames", + "TX Oversize Frames", + "TX Undersize Frames", + "TX Fragmented Frames", +}; + +/* Fill in an array of 64-bit statistics from various sources. + * This array will be appended to the end of the ethtool_stats + * structure, and returned to user space + */ +void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf) +{ + int i; + struct gfar_private *priv = (struct gfar_private *) dev->priv; + u32 *rmon = (u32 *) & priv->regs->rmon; + u64 *extra = (u64 *) & priv->extra_stats; + struct gfar_stats *stats = (struct gfar_stats *) buf; + + for (i = 0; i < GFAR_RMON_LEN; i++) { + stats->rmon[i] = (u64) (rmon[i]); + } + + for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) { + stats->extra[i] = extra[i]; + } +} + +/* Returns the number of stats (and their corresponding strings) */ +int gfar_stats_count(struct net_device *dev) +{ + return GFAR_STATS_LEN; +} + +void gfar_gstrings_normon(struct net_device *dev, u32 stringset, u8 * buf) +{ + memcpy(buf, stat_gstrings, GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN); +} + +void gfar_fill_stats_normon(struct net_device *dev, + struct ethtool_stats *dummy, u64 * buf) +{ + int i; + struct gfar_private *priv = (struct gfar_private *) dev->priv; + u64 *extra = (u64 *) & priv->extra_stats; + + for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) { + buf[i] = extra[i]; + } +} + + +int gfar_stats_count_normon(struct net_device *dev) +{ + return GFAR_EXTRA_STATS_LEN; +} +/* Fills in the drvinfo structure with some basic info */ +void gfar_gdrvinfo(struct net_device *dev, struct + ethtool_drvinfo *drvinfo) +{ + strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN); + strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN); + strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN); + strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN); + drvinfo->n_stats = GFAR_STATS_LEN; + drvinfo->testinfo_len = 0; + drvinfo->regdump_len = 0; + drvinfo->eedump_len = 0; +} + +/* Return the current settings in the ethtool_cmd structure */ +int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct gfar_private *priv = (struct gfar_private *) dev->priv; + uint gigabit_support = + priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0; + uint gigabit_advert = + priv->einfo->flags & GFAR_HAS_GIGABIT ? ADVERTISED_1000baseT_Full: 0; + + cmd->supported = (SUPPORTED_10baseT_Half + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | gigabit_support | SUPPORTED_Autoneg); + + /* For now, we always advertise everything */ + cmd->advertising = (ADVERTISED_10baseT_Half + | ADVERTISED_100baseT_Half + | ADVERTISED_100baseT_Full + | gigabit_advert | ADVERTISED_Autoneg); + + cmd->speed = priv->speed; + cmd->duplex = priv->duplexity; + cmd->port = PORT_MII; + cmd->phy_address = priv->einfo->phyid; + cmd->transceiver = XCVR_EXTERNAL; + cmd->autoneg = AUTONEG_ENABLE; + cmd->maxtxpkt = priv->txcount; + cmd->maxrxpkt = priv->rxcount; + + return 0; +} + +/* Return the length of the register structure */ +int gfar_reglen(struct net_device *dev) +{ + return sizeof (struct gfar); +} + +/* Return a dump of the GFAR register space */ +void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +{ + int i; + struct gfar_private *priv = (struct gfar_private *) dev->priv; + u32 *theregs = (u32 *) priv->regs; + u32 *buf = (u32 *) regbuf; + + for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++) + buf[i] = theregs[i]; +} + +/* Return the link state 1 is up, 0 is down */ +u32 gfar_get_link(struct net_device *dev) +{ + struct gfar_private *priv = (struct gfar_private *) dev->priv; + return (u32) priv->link; +} + +/* Fill in a buffer with the strings which correspond to the + * stats */ +void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf) +{ + memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN); +} + +/* Convert microseconds to ethernet clock ticks, which changes + * depending on what speed the controller is running at */ +static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs) +{ + unsigned int count; + + /* The timer is different, depending on the interface speed */ + switch (priv->speed) { + case 1000: + count = GFAR_GBIT_TIME; + break; + case 100: + count = GFAR_100_TIME; + break; + case 10: + default: + count = GFAR_10_TIME; + break; + } + + /* Make sure we return a number greater than 0 + * if usecs > 0 */ + return ((usecs * 1000 + count - 1) / count); +} + +/* Convert ethernet clock ticks to microseconds */ +static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int ticks) +{ + unsigned int count; + + /* The timer is different, depending on the interface speed */ + switch (priv->speed) { + case 1000: + count = GFAR_GBIT_TIME; + break; + case 100: + count = GFAR_100_TIME; + break; + case 10: + default: + count = GFAR_10_TIME; + break; + } + + /* Make sure we return a number greater than 0 */ + /* if ticks is > 0 */ + return ((ticks * count) / 1000); +} + +/* Get the coalescing parameters, and put them in the cvals + * structure. */ +int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) +{ + struct gfar_private *priv = (struct gfar_private *) dev->priv; + + cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime); + cvals->rx_max_coalesced_frames = priv->rxcount; + + cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, priv->txtime); + cvals->tx_max_coalesced_frames = priv->txcount; + + cvals->use_adaptive_rx_coalesce = 0; + cvals->use_adaptive_tx_coalesce = 0; + + cvals->pkt_rate_low = 0; + cvals->rx_coalesce_usecs_low = 0; + cvals->rx_max_coalesced_frames_low = 0; + cvals->tx_coalesce_usecs_low = 0; + cvals->tx_max_coalesced_frames_low = 0; + + /* When the packet rate is below pkt_rate_high but above + * pkt_rate_low (both measured in packets per second) the + * normal {rx,tx}_* coalescing parameters are used. + */ + + /* When the packet rate is (measured in packets per second) + * is above pkt_rate_high, the {rx,tx}_*_high parameters are + * used. + */ + cvals->pkt_rate_high = 0; + cvals->rx_coalesce_usecs_high = 0; + cvals->rx_max_coalesced_frames_high = 0; + cvals->tx_coalesce_usecs_high = 0; + cvals->tx_max_coalesced_frames_high = 0; + + /* How often to do adaptive coalescing packet rate sampling, + * measured in seconds. Must not be zero. + */ + cvals->rate_sample_interval = 0; + + return 0; +} + +/* Change the coalescing values. + * Both cvals->*_usecs and cvals->*_frames have to be > 0 + * in order for coalescing to be active + */ +int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) +{ + struct gfar_private *priv = (struct gfar_private *) dev->priv; + + /* Set up rx coalescing */ + if ((cvals->rx_coalesce_usecs == 0) || + (cvals->rx_max_coalesced_frames == 0)) + priv->rxcoalescing = 0; + else + priv->rxcoalescing = 1; + + priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs); + priv->rxcount = cvals->rx_max_coalesced_frames; + + /* Set up tx coalescing */ + if ((cvals->tx_coalesce_usecs == 0) || + (cvals->tx_max_coalesced_frames == 0)) + priv->txcoalescing = 0; + else + priv->txcoalescing = 1; + + priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs); + priv->txcount = cvals->tx_max_coalesced_frames; + + if (priv->rxcoalescing) + gfar_write(&priv->regs->rxic, + mk_ic_value(priv->rxcount, priv->rxtime)); + else + gfar_write(&priv->regs->rxic, 0); + + if (priv->txcoalescing) + gfar_write(&priv->regs->txic, + mk_ic_value(priv->txcount, priv->txtime)); + else + gfar_write(&priv->regs->txic, 0); + + return 0; +} + +/* Fills in rvals with the current ring parameters. Currently, + * rx, rx_mini, and rx_jumbo rings are the same size, as mini and + * jumbo are ignored by the driver */ +void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals) +{ + struct gfar_private *priv = (struct gfar_private *) dev->priv; + + rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE; + rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE; + rvals->rx_jumbo_max_pending = GFAR_RX_MAX_RING_SIZE; + rvals->tx_max_pending = GFAR_TX_MAX_RING_SIZE; + + /* Values changeable by the user. The valid values are + * in the range 1 to the "*_max_pending" counterpart above. + */ + rvals->rx_pending = priv->rx_ring_size; + rvals->rx_mini_pending = priv->rx_ring_size; + rvals->rx_jumbo_pending = priv->rx_ring_size; + rvals->tx_pending = priv->tx_ring_size; +} + +/* Change the current ring parameters, stopping the controller if + * necessary so that we don't mess things up while we're in + * motion. We wait for the ring to be clean before reallocating + * the rings. */ +int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) +{ + u32 tempval; + struct gfar_private *priv = (struct gfar_private *) dev->priv; + int err = 0; + + if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE) + return -EINVAL; + + if (!is_power_of_2(rvals->rx_pending)) { + printk("%s: Ring sizes must be a power of 2\n", + dev->name); + return -EINVAL; + } + + if (rvals->tx_pending > GFAR_TX_MAX_RING_SIZE) + return -EINVAL; + + if (!is_power_of_2(rvals->tx_pending)) { + printk("%s: Ring sizes must be a power of 2\n", + dev->name); + return -EINVAL; + } + + /* Stop the controller so we don't rx any more frames */ + /* But first, make sure we clear the bits */ + tempval = gfar_read(&priv->regs->dmactrl); + tempval &= ~(DMACTRL_GRS | DMACTRL_GTS); + gfar_write(&priv->regs->dmactrl, tempval); + + tempval = gfar_read(&priv->regs->dmactrl); + tempval |= (DMACTRL_GRS | DMACTRL_GTS); + gfar_write(&priv->regs->dmactrl, tempval); + + while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC))) + cpu_relax(); + + /* Note that rx is not clean right now */ + priv->rxclean = 0; + + if (dev->flags & IFF_UP) { + /* Tell the driver to process the rest of the frames */ + gfar_receive(0, (void *) dev, NULL); + + /* Now wait for it to be done */ + wait_event_interruptible(priv->rxcleanupq, priv->rxclean); + + /* Ok, all packets have been handled. Now we bring it down, + * change the ring size, and bring it up */ + + stop_gfar(dev); + } + + priv->rx_ring_size = rvals->rx_pending; + priv->tx_ring_size = rvals->tx_pending; + + if (dev->flags & IFF_UP) + err = startup_gfar(dev); + + return err; +} + +struct ethtool_ops gfar_ethtool_ops = { + .get_settings = gfar_gsettings, + .get_drvinfo = gfar_gdrvinfo, + .get_regs_len = gfar_reglen, + .get_regs = gfar_get_regs, + .get_link = gfar_get_link, + .get_coalesce = gfar_gcoalesce, + .set_coalesce = gfar_scoalesce, + .get_ringparam = gfar_gringparam, + .set_ringparam = gfar_sringparam, + .get_strings = gfar_gstrings, + .get_stats_count = gfar_stats_count, + .get_ethtool_stats = gfar_fill_stats, +}; diff --git a/drivers/net/gianfar_phy.c b/drivers/net/gianfar_phy.c new file mode 100644 index 000000000..ea02e5da9 --- /dev/null +++ b/drivers/net/gianfar_phy.c @@ -0,0 +1,622 @@ +/* + * drivers/net/gianfar_phy.c + * + * Gianfar Ethernet Driver -- PHY handling + * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560 + * Based on 8260_io/fcc_enet.c + * + * Author: Andy Fleming + * Maintainer: Kumar Gala (kumar.gala@freescale.com) + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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++; + } +} diff --git a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h new file mode 100644 index 000000000..df4c0ec9d --- /dev/null +++ b/drivers/net/gianfar_phy.h @@ -0,0 +1,202 @@ +/* + * drivers/net/gianfar_phy.h + * + * Gianfar Ethernet Driver -- PHY handling + * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560 + * Based on 8260_io/fcc_enet.c + * + * Author: Andy Fleming + * Maintainer: Kumar Gala (kumar.gala@freescale.com) + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifndef __GIANFAR_PHY_H +#define __GIANFAR_PHY_H + +/* simple datum processing commands */ +#define miim_end (0xffff0000U) +#define miim_read (0x00010000U) +#define miim_clear_bits(reg,x) { reg, (0x00020000U | ~(u32)(x)), NULL } +#define miim_set_bits(reg,x) { reg, (0x00030000U | (u32)(x)), NULL } +#define miim_flip_bits(reg,x) { reg, (0x00040000U | (u32)(x)), NULL } +#define miim_write(reg, x) { reg, (0x0000ffffU & (u32)(x)), NULL } + +#define MIIMIND_BUSY 0x00000001 +#define MIIMIND_NOTVALID 0x00000004 + +#define MIIM_CONTROL 0x00 +#define MIIM_CONTROL_RESET 0x00008000 +#define MIIM_CONTROL_INIT 0x00001140 +#define MIIM_ANEN 0x00001000 + +#define MIIM_CR 0x00 +#define MIIM_CR_RST 0x00008000 +#define MIIM_CR_INIT 0x00001000 + +#define MIIM_STATUS 0x1 +#define MIIM_STATUS_AN_DONE 0x00000020 +#define MIIM_STATUS_LINK 0x0004 + +#define MIIM_PHYIR1 0x2 +#define MIIM_PHYIR2 0x3 + +#define GFAR_AN_TIMEOUT 0x000fffff + +#define MIIM_ANLPBPA 0x5 +#define MIIM_ANLPBPA_HALF 0x00000040 +#define MIIM_ANLPBPA_FULL 0x00000020 + +#define MIIM_ANEX 0x6 +#define MIIM_ANEX_NP 0x00000004 +#define MIIM_ANEX_PRX 0x00000002 + + +/* Cicada Extended Control Register 1 */ +#define MIIM_CIS8201_EXT_CON1 0x17 +#define MIIM_CIS8201_EXTCON1_INIT 0x0000 + +/* Cicada Interrupt Mask Register */ +#define MIIM_CIS8204_IMASK 0x19 +#define MIIM_CIS8204_IMASK_IEN 0x8000 +#define MIIM_CIS8204_IMASK_SPEED 0x4000 +#define MIIM_CIS8204_IMASK_LINK 0x2000 +#define MIIM_CIS8204_IMASK_DUPLEX 0x1000 +#define MIIM_CIS8204_IMASK_MASK 0xf000 + +/* Cicada Interrupt Status Register */ +#define MIIM_CIS8204_ISTAT 0x1a +#define MIIM_CIS8204_ISTAT_STATUS 0x8000 +#define MIIM_CIS8204_ISTAT_SPEED 0x4000 +#define MIIM_CIS8204_ISTAT_LINK 0x2000 +#define MIIM_CIS8204_ISTAT_DUPLEX 0x1000 + +/* Cicada Auxiliary Control/Status Register */ +#define MIIM_CIS8201_AUX_CONSTAT 0x1c +#define MIIM_CIS8201_AUXCONSTAT_INIT 0x0004 +#define MIIM_CIS8201_AUXCONSTAT_DUPLEX 0x0020 +#define MIIM_CIS8201_AUXCONSTAT_SPEED 0x0018 +#define MIIM_CIS8201_AUXCONSTAT_GBIT 0x0010 +#define MIIM_CIS8201_AUXCONSTAT_100 0x0008 + +/* 88E1011 PHY Status Register */ +#define MIIM_88E1011_PHY_STATUS 0x11 +#define MIIM_88E1011_PHYSTAT_SPEED 0xc000 +#define MIIM_88E1011_PHYSTAT_GBIT 0x8000 +#define MIIM_88E1011_PHYSTAT_100 0x4000 +#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000 +#define MIIM_88E1011_PHYSTAT_LINK 0x0400 + +#define MIIM_88E1011_IEVENT 0x13 +#define MIIM_88E1011_IEVENT_CLEAR 0x0000 + +#define MIIM_88E1011_IMASK 0x12 +#define MIIM_88E1011_IMASK_INIT 0x6400 +#define MIIM_88E1011_IMASK_CLEAR 0x0000 + +/* DM9161 Control register values */ +#define MIIM_DM9161_CR_STOP 0x0400 +#define MIIM_DM9161_CR_RSTAN 0x1200 + +#define MIIM_DM9161_SCR 0x10 +#define MIIM_DM9161_SCR_INIT 0x0610 + +/* DM9161 Specified Configuration and Status Register */ +#define MIIM_DM9161_SCSR 0x11 +#define MIIM_DM9161_SCSR_100F 0x8000 +#define MIIM_DM9161_SCSR_100H 0x4000 +#define MIIM_DM9161_SCSR_10F 0x2000 +#define MIIM_DM9161_SCSR_10H 0x1000 + +/* DM9161 Interrupt Register */ +#define MIIM_DM9161_INTR 0x15 +#define MIIM_DM9161_INTR_PEND 0x8000 +#define MIIM_DM9161_INTR_DPLX_MASK 0x0800 +#define MIIM_DM9161_INTR_SPD_MASK 0x0400 +#define MIIM_DM9161_INTR_LINK_MASK 0x0200 +#define MIIM_DM9161_INTR_MASK 0x0100 +#define MIIM_DM9161_INTR_DPLX_CHANGE 0x0010 +#define MIIM_DM9161_INTR_SPD_CHANGE 0x0008 +#define MIIM_DM9161_INTR_LINK_CHANGE 0x0004 +#define MIIM_DM9161_INTR_INIT 0x0000 +#define MIIM_DM9161_INTR_STOP \ +(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \ + | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK) + +/* DM9161 10BT Configuration/Status */ +#define MIIM_DM9161_10BTCSR 0x12 +#define MIIM_DM9161_10BTCSR_INIT 0x7800 + +/* BCM54xx regs */ +#define MIIM_BCM54xx_AUXCONTROL 0x18 +#define MIIM_BCM54xx_AUXSTATUS 0x19 +#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK 0x0700 +#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT 8 + +#define MIIM_READ_COMMAND 0x00000001 + +/* + * struct phy_cmd: A command for reading or writing a PHY register + * + * mii_reg: The register to read or write + * + * mii_data: For writes, the value to put in the register. + * A value of -1 indicates this is a read. + * + * funct: A function pointer which is invoked for each command. + * For reads, this function will be passed the value read + * from the PHY, and process it. + * For writes, the result of this function will be written + * to the PHY register + */ +struct phy_cmd { + u32 mii_reg; + u32 mii_data; + u16 (*funct) (u16 mii_reg, struct net_device * dev); +}; + +/* struct phy_info: a structure which defines attributes for a PHY + * + * id will contain a number which represents the PHY. During + * startup, the driver will poll the PHY to find out what its + * UID--as defined by registers 2 and 3--is. The 32-bit result + * gotten from the PHY will be shifted right by "shift" bits to + * discard any bits which may change based on revision numbers + * unimportant to functionality + * + * The struct phy_cmd entries represent pointers to an arrays of + * commands which tell the driver what to do to the PHY. + */ +struct phy_info { + u32 id; + char *name; + unsigned int shift; + /* Called to configure the PHY, and modify the controller + * based on the results */ + const struct phy_cmd *config; + + /* Called when starting up the controller. Usually sets + * up the interrupt for state changes */ + const struct phy_cmd *startup; + + /* Called inside the interrupt handler to acknowledge + * the interrupt */ + const struct phy_cmd *ack_int; + + /* Called in the bottom half to handle the interrupt */ + const struct phy_cmd *handle_int; + + /* Called when bringing down the controller. Usually stops + * the interrupts from being generated */ + const struct phy_cmd *shutdown; +}; + +struct phy_info *get_phy_info(struct net_device *dev); +void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd); + +#endif /* GIANFAR_PHY_H */ diff --git a/drivers/serial/mpsc/Makefile b/drivers/serial/mpsc/Makefile new file mode 100644 index 000000000..7d9054dd5 --- /dev/null +++ b/drivers/serial/mpsc/Makefile @@ -0,0 +1,6 @@ +# +# Make file for the Marvell MPSC driver. +# + +obj-$(CONFIG_SERIAL_MPSC) += mpsc.o +obj-$(CONFIG_PPC32) += mpsc_ppc32.o diff --git a/drivers/serial/mpsc/mpsc.c b/drivers/serial/mpsc/mpsc.c new file mode 100644 index 000000000..de7f97cfa --- /dev/null +++ b/drivers/serial/mpsc/mpsc.c @@ -0,0 +1,1455 @@ +/* + * drivers/serial/mpsc/mpsc.c + * + * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240, + * GT64260, MV64340, MV64360, GT96100, ... ). + * + * Author: Mark A. Greer + * + * 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; ibufsize = 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; ilink = 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; iport.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; iport.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; itxr_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; iport.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; jline].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 "); +MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver $Revision: 1.00 $"); +MODULE_VERSION(MPSC_VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(MPSC_MAJOR); diff --git a/drivers/serial/mpsc/mpsc.h b/drivers/serial/mpsc/mpsc.h new file mode 100644 index 000000000..400629ea4 --- /dev/null +++ b/drivers/serial/mpsc/mpsc.h @@ -0,0 +1,257 @@ +/* + * drivers/serial/mpsc/mpsc.h + * + * Author: Mark A. Greer + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include "mpsc_defs.h" + + +/* + * Descriptors and buffers must be cache line aligned. + * Buffers lengths must be multiple of cache line size. + * Number of Tx & Rx descriptors must be power of 2. + */ +#define MPSC_DESC_ALIGN dma_get_cache_alignment() +#define MPSC_BUF_ALIGN dma_get_cache_alignment() + +#define MPSC_RXR_ENTRIES 32 +#define MPSC_RXRE_SIZE sizeof(mpsc_rx_desc_t) +#define MPSC_RXR_SIZE (MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE) +#define MPSC_RXBE_SIZE dma_get_cache_alignment() +#define MPSC_RXB_SIZE (MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE) + +#define MPSC_TXR_ENTRIES 32 +#define MPSC_TXRE_SIZE sizeof(mpsc_tx_desc_t) +#define MPSC_TXR_SIZE (MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE) +#define MPSC_TXBE_SIZE dma_get_cache_alignment() +#define MPSC_TXB_SIZE (MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE) + +typedef struct { + u16 bufsize; + u16 bytecnt; + u32 cmdstat; + u32 link; + u32 buf_ptr; +} mpsc_rx_desc_t __attribute((packed)); + +/* Tx and Rx Ring entry descriptors */ +typedef struct { + u16 bytecnt; + u16 shadow; + u32 cmdstat; + u32 link; + u32 buf_ptr; +} mpsc_tx_desc_t __attribute((packed)); + +/* The main driver data structure */ +typedef struct { + struct uart_port port; /* Overlay uart_port structure */ + + /* Internal driver state for this ctlr */ + u8 ready; + u8 rcv_data; + tcflag_t c_iflag; /* save termios->c_iflag */ + tcflag_t c_cflag; /* save termios->c_cflag */ + + /* Info passed in from platform */ + u8 mirror_regs; /* Need to mirror regs? */ + u8 cache_mgmt; /* Need manual cache mgmt? */ + u8 brg_can_tune; /* BRG has baud tuning? */ + u32 brg_clk_src; + u16 mpsc_max_idle; + int default_baud; + int default_bits; + int default_parity; + int default_flow; + + /* Physical addresses of various blocks of registers (from platform) */ + u32 mpsc_base_p; + u32 mpsc_routing_base_p; + u32 sdma_base_p; + u32 sdma_intr_base_p; + u32 brg_base_p; + + /* Virtual addresses of various blocks of registers (from platform) */ + u32 mpsc_base; + u32 mpsc_routing_base; + u32 sdma_base; + u32 sdma_intr_base; + u32 brg_base; + + /* Descriptor ring and buffer allocations */ + void *desc_region; /* Region for desc rings */ + dma_addr_t desc_region_p; + u32 desc_region_size; + + void *buf_region; /* kmalloc region for bufs */ + u32 buf_region_size; + + mpsc_rx_desc_t *rxr; /* Rx descriptor ring */ + mpsc_rx_desc_t *rxr_p; /* Phys addr of rxr */ + u32 rxr_posn; /* First desc w/ Rx data */ + u8 *rxb; /* Rx Ring I/O buf */ + dma_addr_t rxb_p; /* Phys addr of rxb */ + + mpsc_tx_desc_t *txr; /* Tx descriptor ring */ + mpsc_tx_desc_t *txr_p; /* Phys addr of txr */ + u32 txr_posn; /* First unused desc */ + u8 *txb; /* Tx Ring I/O buf */ + dma_addr_t txb_p; /* Phys addr of txb */ + + /* Mirrored values of regs we can't read (if 'mirror_regs' set) */ + u32 MPSC_CHR_1_m; + u32 MPSC_CHR_2_m; + u32 MPSC_CHR_10_m; + u32 MPSC_MPCR_m; + u32 MPSC_MRR_m; + u32 MPSC_RCRR_m; + u32 MPSC_TCRR_m; + u32 SDMA_INTR_MASK_m; + u32 BRG_BCR_m; +} mpsc_port_info_t; + +/* + * Some MPSC ctlrs have an erratum where they aren't supposed to access + * cache coherent memory regions. From practical experience, the erratum + * is not triggered as long as there isn't a snoop hit. Therefore, if + * the MPSC in used has this erratum and coherency is enabled on the platform, + * we must manually manage the cache for ring descriptors and the I/O buffers. + */ +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) +#define MPSC_CACHE_FLUSH(pi, s, e) { \ + if (pi->cache_mgmt) { \ + /* 64x60 erratum: can't use dcbst/clean_dcache_range() */ \ + flush_dcache_range((ulong)s, (ulong)e); \ + mb(); \ + } \ +} + +#define MPSC_CACHE_INVALIDATE(pi, s, e) { \ + if (pi->cache_mgmt) { \ + invalidate_dcache_range((ulong)s, (ulong)e); \ + mb(); \ + } \ +} + +#define MPSC_CACHE_FLUSH_INVALIDATE(pi, s, e) { \ + if (pi->cache_mgmt) { \ + flush_dcache_range((ulong)s, (ulong)e); \ + mb(); \ + } \ +} +#else +#define MPSC_CACHE_FLUSH(pi, s, e) +#define MPSC_CACHE_INVALIDATE(pi, s, e) +#define MPSC_CACHE_FLUSH_INVALIDATE(pi, s, e) +#endif + +/* + * 'MASK_INSERT' takes the low-order 'n' bits of 'i', shifts it 'b' bits to + * the left, and inserts it into the target 't'. The corresponding bits in + * 't' will have been cleared before the bits in 'i' are inserted. + */ +#ifdef CONFIG_PPC32 +#define MASK_INSERT(t, i, n, b) ({ \ + u32 rval = (t); \ + __asm__ __volatile__( \ + "rlwimi %0,%2,%4,32-(%3+%4),31-%4\n" \ + : "=r" (rval) \ + : "0" (rval), "r" (i), "i" (n), "i" (b)); \ + rval; \ +}) +#else +/* These macros are really just examples. Feel free to change them --MAG */ +#define GEN_MASK(n, b) \ +({ \ + u32 m, sl, sr; \ + sl = 32 - (n); \ + sr = sl - (b); \ + m = (0xffffffff << sl) >> sr; \ +}) + +#define MASK_INSERT(t, i, n, b) \ +({ \ + u32 m, rval = (t); \ + m = GEN_MASK((n), (b)); \ + rval &= ~m; \ + rval |= (((i) << (b)) & m); \ +}) +#endif + +/* I/O macros for regs that you can read */ +#define MPSC_READ(pi, unit, offset) readl((pi)->unit##_base + (offset)) +#define MPSC_WRITE(pi, unit, offset, v) writel(v, (pi)->unit##_base + (offset)) +#define MPSC_MOD_FIELD(pi, unit, offset, num_bits, shift, val) \ +{ \ + u32 v; \ + v = readl((pi)->unit##_base + (offset)); \ + writel(MASK_INSERT(v,val,num_bits,shift), (pi)->unit##_base+(offset));\ +} + +#define MPSC_READ_M(pi, unit, offset) \ +({ \ + u32 v; \ + if ((pi)->mirror_regs) v = (pi)->offset##_m; \ + else v = readl((pi)->unit##_base + (offset)); \ + v; \ +}) + +#define MPSC_WRITE_M(pi, unit, offset, v) \ +({ \ + if ((pi)->mirror_regs) (pi)->offset##_m = v; \ + writel(v, (pi)->unit##_base + (offset)); \ +}) + +#define MPSC_MOD_FIELD_M(pi, unit, offset, num_bits, shift, val) \ +({ \ + u32 v; \ + if ((pi)->mirror_regs) v = (pi)->offset##_m; \ + else v = readl((pi)->unit##_base + (offset)); \ + v = MASK_INSERT(v, val, num_bits, shift); \ + if ((pi)->mirror_regs) (pi)->offset##_m = v; \ + writel(v, (pi)->unit##_base + (offset)); \ +}) + +#if !defined(MIN) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +/* Hooks to platform-specific code */ +int mpsc_platform_register_driver(void); +void mpsc_platform_unregister_driver(void); + +/* Hooks back in to mpsc common to be called by platform-specific code */ +mpsc_port_info_t *mpsc_device_probe(int index); +mpsc_port_info_t *mpsc_device_remove(int index); + +#endif /* __MPSC_H__ */ diff --git a/drivers/serial/mpsc/mpsc_defs.h b/drivers/serial/mpsc/mpsc_defs.h new file mode 100644 index 000000000..7bf095a9e --- /dev/null +++ b/drivers/serial/mpsc/mpsc_defs.h @@ -0,0 +1,153 @@ +/* + * drivers/serial/mpsc/mpsc_defs.h + * + * Register definitions for the Marvell Multi-Protocol Serial Controller (MPSC), + * Serial DMA Controller (SDMA), and Baud Rate Generator (BRG). + * + * Author: Mark A. Greer + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef __MPSC_DEFS_H__ +#define __MPSC_DEFS_H__ + +#define MPSC_NUM_CTLRS 2 + + +/* + ***************************************************************************** + * + * Multi-Protocol Serial Controller Interface Registers + * + ***************************************************************************** + */ + +/* Main Configuratino Register Offsets */ +#define MPSC_MMCRL 0x0000 +#define MPSC_MMCRH 0x0004 +#define MPSC_MPCR 0x0008 +#define MPSC_CHR_1 0x000c +#define MPSC_CHR_2 0x0010 +#define MPSC_CHR_3 0x0014 +#define MPSC_CHR_4 0x0018 +#define MPSC_CHR_5 0x001c +#define MPSC_CHR_6 0x0020 +#define MPSC_CHR_7 0x0024 +#define MPSC_CHR_8 0x0028 +#define MPSC_CHR_9 0x002c +#define MPSC_CHR_10 0x0030 +#define MPSC_CHR_11 0x0034 +#define MPSC_REG_BLOCK_SIZE 0x0038 + + +#define MPSC_MPCR_CL_5 0 +#define MPSC_MPCR_CL_6 1 +#define MPSC_MPCR_CL_7 2 +#define MPSC_MPCR_CL_8 3 +#define MPSC_MPCR_SBL_1 0 +#define MPSC_MPCR_SBL_2 3 + +#define MPSC_CHR_2_TEV (1<<1) +#define MPSC_CHR_2_TA (1<<7) +#define MPSC_CHR_2_TTCS (1<<9) +#define MPSC_CHR_2_REV (1<<17) +#define MPSC_CHR_2_RA (1<<23) +#define MPSC_CHR_2_CRD (1<<25) +#define MPSC_CHR_2_EH (1<<31) +#define MPSC_CHR_2_PAR_ODD 0 +#define MPSC_CHR_2_PAR_SPACE 1 +#define MPSC_CHR_2_PAR_EVEN 2 +#define MPSC_CHR_2_PAR_MARK 3 + +/* MPSC Signal Routing */ +#define MPSC_MRR 0x0000 +#define MPSC_RCRR 0x0004 +#define MPSC_TCRR 0x0008 +#define MPSC_ROUTING_REG_BLOCK_SIZE 0x000c + +/* + ***************************************************************************** + * + * Serial DMA Controller Interface Registers + * + ***************************************************************************** + */ + +#define SDMA_SDC 0x0000 +#define SDMA_SDCM 0x0008 +#define SDMA_RX_DESC 0x0800 +#define SDMA_RX_BUF_PTR 0x0808 +#define SDMA_SCRDP 0x0810 +#define SDMA_TX_DESC 0x0c00 +#define SDMA_SCTDP 0x0c10 +#define SDMA_SFTDP 0x0c14 +#define SDMA_REG_BLOCK_SIZE 0x0c18 + +#define SDMA_DESC_CMDSTAT_PE (1<<0) +#define SDMA_DESC_CMDSTAT_CDL (1<<1) +#define SDMA_DESC_CMDSTAT_FR (1<<3) +#define SDMA_DESC_CMDSTAT_OR (1<<6) +#define SDMA_DESC_CMDSTAT_BR (1<<9) +#define SDMA_DESC_CMDSTAT_MI (1<<10) +#define SDMA_DESC_CMDSTAT_A (1<<11) +#define SDMA_DESC_CMDSTAT_AM (1<<12) +#define SDMA_DESC_CMDSTAT_CT (1<<13) +#define SDMA_DESC_CMDSTAT_C (1<<14) +#define SDMA_DESC_CMDSTAT_ES (1<<15) +#define SDMA_DESC_CMDSTAT_L (1<<16) +#define SDMA_DESC_CMDSTAT_F (1<<17) +#define SDMA_DESC_CMDSTAT_P (1<<18) +#define SDMA_DESC_CMDSTAT_EI (1<<23) +#define SDMA_DESC_CMDSTAT_O (1<<31) + +#define SDMA_DESC_DFLT (SDMA_DESC_CMDSTAT_O | \ + SDMA_DESC_CMDSTAT_EI) + +#define SDMA_SDC_RFT (1<<0) +#define SDMA_SDC_SFM (1<<1) +#define SDMA_SDC_BLMR (1<<6) +#define SDMA_SDC_BLMT (1<<7) +#define SDMA_SDC_POVR (1<<8) +#define SDMA_SDC_RIFB (1<<9) + +#define SDMA_SDCM_ERD (1<<7) +#define SDMA_SDCM_AR (1<<15) +#define SDMA_SDCM_STD (1<<16) +#define SDMA_SDCM_TXD (1<<23) +#define SDMA_SDCM_AT (1<<31) + +#define SDMA_0_CAUSE_RXBUF (1<<0) +#define SDMA_0_CAUSE_RXERR (1<<1) +#define SDMA_0_CAUSE_TXBUF (1<<2) +#define SDMA_0_CAUSE_TXEND (1<<3) +#define SDMA_1_CAUSE_RXBUF (1<<8) +#define SDMA_1_CAUSE_RXERR (1<<9) +#define SDMA_1_CAUSE_TXBUF (1<<10) +#define SDMA_1_CAUSE_TXEND (1<<11) + +#define SDMA_CAUSE_RX_MASK (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR | \ + SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR) +#define SDMA_CAUSE_TX_MASK (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND | \ + SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND) + +/* SDMA Interrupt registers */ +#define SDMA_INTR_CAUSE 0x0000 +#define SDMA_INTR_MASK 0x0080 +#define SDMA_INTR_REG_BLOCK_SIZE 0x0084 + +/* + ***************************************************************************** + * + * Baud Rate Generator Interface Registers + * + ***************************************************************************** + */ + +#define BRG_BCR 0x0000 +#define BRG_BTR 0x0004 +#define BRG_REG_BLOCK_SIZE 0x0008 + +#endif /*__MPSC_DEFS_H__ */ diff --git a/drivers/serial/mpsc/mpsc_ppc32.c b/drivers/serial/mpsc/mpsc_ppc32.c new file mode 100644 index 000000000..53b1dd4fa --- /dev/null +++ b/drivers/serial/mpsc/mpsc_ppc32.c @@ -0,0 +1,110 @@ +/* + * drivers/serial/mpsc/mpsc_ppc32.c + * + * Middle layer that sucks data from the ppc32 OCP--that is, chip & + * platform-specific data--and puts it into the mpsc_port_info_t structure + * for the mpsc driver to use. + * + * Author: Mark A. Greer + * + * 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 +#include + +static void mpsc_ocp_remove(struct ocp_device *ocpdev); + +static int +mpsc_ocp_probe(struct ocp_device *ocpdev) +{ + mpsc_port_info_t *pi; + mv64x60_ocp_mpsc_data_t *dp; + u32 base; + int rc = -ENODEV; + + if ((pi = mpsc_device_probe(ocpdev->def->index)) != NULL) { + dp = (mv64x60_ocp_mpsc_data_t *)ocpdev->def->additions; + + pi->mpsc_base_p = ocpdev->def->paddr; + + if (ocpdev->def->index == 0) { + base = pi->mpsc_base_p - MV64x60_MPSC_0_OFFSET; + pi->sdma_base_p = base + MV64x60_SDMA_0_OFFSET; + pi->brg_base_p = base + MV64x60_BRG_0_OFFSET; + } + else { /* Must be 1 */ + base = pi->mpsc_base_p - MV64x60_MPSC_1_OFFSET; + pi->sdma_base_p = base + MV64x60_SDMA_1_OFFSET; + pi->brg_base_p = base + MV64x60_BRG_1_OFFSET; + } + + pi->mpsc_routing_base_p = base + MV64x60_MPSC_ROUTING_OFFSET; + pi->sdma_intr_base_p = base + MV64x60_SDMA_INTR_OFFSET; + + pi->port.irq = dp->sdma_irq; + pi->port.uartclk = dp->brg_clk_freq; + + pi->mirror_regs = dp->mirror_regs; + pi->cache_mgmt = dp->cache_mgmt; + pi->brg_can_tune = dp->brg_can_tune; + pi->brg_clk_src = dp->brg_clk_src; + pi->mpsc_max_idle = dp->max_idle; + pi->default_baud = dp->default_baud; + pi->default_bits = dp->default_bits; + pi->default_parity = dp->default_parity; + pi->default_flow = dp->default_flow; + + /* Initial values of mirrored regs */ + pi->MPSC_CHR_1_m = dp->chr_1_val; + pi->MPSC_CHR_2_m = dp->chr_2_val; + pi->MPSC_CHR_10_m = dp->chr_10_val; + pi->MPSC_MPCR_m = dp->mpcr_val; + pi->MPSC_MRR_m = dp->mrr_val; + pi->MPSC_RCRR_m = dp->rcrr_val; + pi->MPSC_TCRR_m = dp->tcrr_val; + pi->SDMA_INTR_MASK_m = dp->intr_mask_val; + pi->BRG_BCR_m = dp->bcr_val; + + pi->port.iotype = UPIO_MEM; + + rc = 0; + } + + return rc; +} + +static void +mpsc_ocp_remove(struct ocp_device *ocpdev) +{ + (void)mpsc_device_remove(ocpdev->def->index); + return; +} + +static struct ocp_device_id mpsc_ocp_ids[] = { + {.vendor = OCP_VENDOR_MARVELL, .function = OCP_FUNC_MPSC}, + {.vendor = OCP_VENDOR_INVALID} +}; + +static struct ocp_driver mpsc_ocp_driver = { + .name = "mpsc", + .id_table = mpsc_ocp_ids, + .probe = mpsc_ocp_probe, + .remove = mpsc_ocp_remove, +}; + +int +mpsc_platform_register_driver(void) +{ + return ocp_register_driver(&mpsc_ocp_driver); +} + +void +mpsc_platform_unregister_driver(void) +{ + ocp_unregister_driver(&mpsc_ocp_driver); + return; +} diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h new file mode 100644 index 000000000..5e3445da3 --- /dev/null +++ b/fs/jffs2/compr.h @@ -0,0 +1,122 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi , + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +#define JFFS2_RUBINMIPS_PRIORITY 10 +#define JFFS2_DYNRUBIN_PRIORITY 20 +#define JFFS2_LZARI_PRIORITY 30 +#define JFFS2_LZO_PRIORITY 40 +#define JFFS2_RTIME_PRIORITY 50 +#define JFFS2_ZLIB_PRIORITY 60 + +#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ +#define JFFS2_DYNRUBIN_DISABLED /* for decompression */ + +#define JFFS2_COMPR_MODE_NONE 0 +#define JFFS2_COMPR_MODE_PRIORITY 1 +#define JFFS2_COMPR_MODE_SIZE 2 + +void jffs2_set_compression_mode(int mode); +int jffs2_get_compression_mode(void); + +struct jffs2_compressor { + struct list_head list; + int priority; /* used by prirority comr. mode */ + char *name; + char compr; /* JFFS2_COMPR_XXX */ + int (*compress)(unsigned char *data_in, unsigned char *cpage_out, + uint32_t *srclen, uint32_t *destlen, void *model); + int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, + uint32_t cdatalen, uint32_t datalen, void *model); + int usecount; + int disabled; /* if seted the compressor won't compress */ + unsigned char *compr_buf; /* used by size compr. mode */ + uint32_t compr_buf_size; /* used by size compr. mode */ + uint32_t stat_compr_orig_size; + uint32_t stat_compr_new_size; + uint32_t stat_compr_blocks; + uint32_t stat_decompr_blocks; +}; + +int jffs2_register_compressor(struct jffs2_compressor *comp); +int jffs2_unregister_compressor(struct jffs2_compressor *comp); + +int jffs2_compressors_init(void); +int jffs2_compressors_exit(void); + +uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + unsigned char *data_in, unsigned char **cpage_out, + uint32_t *datalen, uint32_t *cdatalen); + +int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + uint16_t comprtype, unsigned char *cdata_in, + unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); + +void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); + +#ifdef CONFIG_JFFS2_PROC +int jffs2_enable_compressor_name(const char *name); +int jffs2_disable_compressor_name(const char *name); +int jffs2_set_compression_mode_name(const char *mode_name); +char *jffs2_get_compression_mode_name(void); +int jffs2_set_compressor_priority(const char *mode_name, int priority); +char *jffs2_list_compressors(void); +char *jffs2_stats(void); +#endif + +/* Compressor modules */ +/* These functions will be called by jffs2_compressors_init/exit */ + +#ifdef CONFIG_JFFS2_RUBIN +int jffs2_rubinmips_init(void); +void jffs2_rubinmips_exit(void); +int jffs2_dynrubin_init(void); +void jffs2_dynrubin_exit(void); +#endif +#ifdef CONFIG_JFFS2_RTIME +int jffs2_rtime_init(void); +void jffs2_rtime_exit(void); +#endif +#ifdef CONFIG_JFFS2_ZLIB +int jffs2_zlib_init(void); +void jffs2_zlib_exit(void); +#endif +#ifdef CONFIG_JFFS2_LZARI +int jffs2_lzari_init(void); +void jffs2_lzari_exit(void); +#endif +#ifdef CONFIG_JFFS2_LZO +int jffs2_lzo_init(void); +void jffs2_lzo_exit(void); +#endif + +/* Prototypes from proc.c */ +int jffs2_proc_init(void); +int jffs2_proc_exit(void); + +#endif /* __JFFS2_COMPR_H__ */ diff --git a/fs/jffs2/proc.c b/fs/jffs2/proc.c new file mode 100644 index 000000000..36c0a0e78 --- /dev/null +++ b/fs/jffs2/proc.c @@ -0,0 +1,217 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Ferenc Havasi , + * 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 +#include +#include +#include +#include +#include +#include +#include +#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)=strlen(stat)) *eof = 1; + else *eof = 0; + kfree(stat); + return len; +} + + +/* Read the JFFS2 'compr_mode' file */ + +static int jffs2_proc_mode_read (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + if (strlen(jffs2_get_compression_mode_name())+1>count) { + /* it should not happen */ + *eof = 1; + return 0; + } + len += sprintf(page, "%s\n",jffs2_get_compression_mode_name()); + *eof = 1; + return len; +} + +/* Write the JFFS2 'compr_mode' file + * sets the actual compression mode + */ + +static int jffs2_proc_mode_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char *compr_name; + + /* collect the name of the compression mode and set it */ + compr_name = kmalloc(count+1,GFP_KERNEL); + if (sscanf(buffer,"%s",compr_name)>0) { + if (jffs2_set_compression_mode_name(compr_name)) { + printk(KERN_WARNING "JFFS2: error switching compression mode. Invalid parameter (%s)?\n",compr_name); + } + } + else { + printk(KERN_WARNING "JFFS2: error: parameter missing\n"); + } + kfree(compr_name); + return count; +} + +/* Read the JFFS2 'compr_list' file */ + +static int jffs2_proc_list_read (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + char *list = jffs2_list_compressors(); + if (strlen(list)+1>count) { + /* it should not happen */ + *eof = 1; + kfree(list); + return 0; + } + len += sprintf(page,"%s",list); + *eof = 1; + kfree(list); + return len; +} + +/* Write the JFFS2 'compr_list' file + * enable/disable a compressor or set the priority of it + */ + +static int jffs2_proc_list_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int prior; + char *compr_name,*compr_cmd; + + compr_name = kmalloc(count+1,GFP_KERNEL); + compr_cmd = kmalloc(count+1,GFP_KERNEL); + if (!compr_name) { + printk(KERN_WARNING "JFFS2: unable to allocate memory\n"); + goto list_write_end; + } + compr_name[0] = 0; + + if (sscanf(buffer,"priority %d %s",&prior,compr_name)>1) { + jffs2_set_compressor_priority(compr_name, prior); + goto list_write_end; + } + if (sscanf(buffer,"enable %s",compr_name)>0) { + jffs2_enable_compressor_name(compr_name); + goto list_write_end; + } + if (sscanf(buffer,"disable %s",compr_name)>0) { + jffs2_disable_compressor_name(compr_name); + goto list_write_end; + } + printk(KERN_WARNING "JFFS2: usage of /proc/fs/jffs2/compr_list:\n" + " echo \"enable COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n" + " echo \"disable COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n" + " echo \"priority NEW_PRIORITY COMPRESSOR_NAME\" >/proc/fs/jffs2/compr_list\n"); +list_write_end: + kfree(compr_cmd); + kfree(compr_name); + return count; +} + +/* Register a JFFS2 proc directory */ + +int jffs2_proc_init(void) +{ + jffs2_proc_root = proc_mkdir("jffs2", proc_root_fs); + + /* create entry for 'compr_stat' file */ + if ((jffs2_proc_compr_stat = create_proc_entry ("compr_stat", 0, jffs2_proc_root))) { + jffs2_proc_compr_stat->read_proc = jffs2_proc_stat_read; + } + else { + return -ENOMEM; + } + /* create entry for 'compr_mode' file */ + if ((jffs2_proc_compr_mode = create_proc_entry ("compr_mode", 0, jffs2_proc_root))) { + jffs2_proc_compr_mode->read_proc = jffs2_proc_mode_read; + jffs2_proc_compr_mode->write_proc = jffs2_proc_mode_write; + } + else { + return -ENOMEM; + } + /* create entry for 'compr_list' file */ + if ((jffs2_proc_compr_mode = create_proc_entry ("compr_list", 0, jffs2_proc_root))) { + jffs2_proc_compr_mode->read_proc = jffs2_proc_list_read; + jffs2_proc_compr_mode->write_proc = jffs2_proc_list_write; + } + else { + return -ENOMEM; + } + return 0; +} + + +/* Unregister a JFFS2 proc directory */ + +int jffs2_proc_exit(void) +{ +#if LINUX_VERSION_CODE < 0x020300 + remove_proc_entry ("compr_stat", &jffs2_proc_root); + remove_proc_entry ("compr_mode", &jffs2_proc_root); + remove_proc_entry ("compr_list", &jffs2_proc_root); + remove_proc_entry ("jffs2", &proc_root_fs); +#else + remove_proc_entry ("compr_stat", jffs2_proc_root); + remove_proc_entry ("compr_mode", jffs2_proc_root); + remove_proc_entry ("compr_list", jffs2_proc_root); + remove_proc_entry ("jffs2", proc_root_fs); +#endif + return 0; +} diff --git a/include/asm-ppc/mv64x60.h b/include/asm-ppc/mv64x60.h new file mode 100644 index 000000000..0aa548226 --- /dev/null +++ b/include/asm-ppc/mv64x60.h @@ -0,0 +1,335 @@ +/* + * include/asm-ppc/mv64x60.h + * + * Prototypes, etc. for the Marvell/Galileo MV64x60 host bridge routines. + * + * Author: Mark A. Greer + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern u8 mv64x60_pci_exclude_bridge; + +extern spinlock_t mv64x60_lock; +extern spinlock_t mv64x60_rmw_lock; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* 32-bit Window table entry defines */ +#define MV64x60_CPU2MEM_0_WIN 0 +#define MV64x60_CPU2MEM_1_WIN 1 +#define MV64x60_CPU2MEM_2_WIN 2 +#define MV64x60_CPU2MEM_3_WIN 3 +#define MV64x60_CPU2DEV_0_WIN 4 +#define MV64x60_CPU2DEV_1_WIN 5 +#define MV64x60_CPU2DEV_2_WIN 6 +#define MV64x60_CPU2DEV_3_WIN 7 +#define MV64x60_CPU2BOOT_WIN 8 +#define MV64x60_CPU2PCI0_IO_WIN 9 +#define MV64x60_CPU2PCI0_MEM_0_WIN 10 +#define MV64x60_CPU2PCI0_MEM_1_WIN 11 +#define MV64x60_CPU2PCI0_MEM_2_WIN 12 +#define MV64x60_CPU2PCI0_MEM_3_WIN 13 +#define MV64x60_CPU2PCI1_IO_WIN 14 +#define MV64x60_CPU2PCI1_MEM_0_WIN 15 +#define MV64x60_CPU2PCI1_MEM_1_WIN 16 +#define MV64x60_CPU2PCI1_MEM_2_WIN 17 +#define MV64x60_CPU2PCI1_MEM_3_WIN 18 +#define MV64x60_CPU2SRAM_WIN 19 +#define MV64x60_CPU2PCI0_IO_REMAP_WIN 20 +#define MV64x60_CPU2PCI1_IO_REMAP_WIN 21 +#define MV64x60_CPU_PROT_0_WIN 22 +#define MV64x60_CPU_PROT_1_WIN 23 +#define MV64x60_CPU_PROT_2_WIN 24 +#define MV64x60_CPU_PROT_3_WIN 25 +#define MV64x60_CPU_SNOOP_0_WIN 26 +#define MV64x60_CPU_SNOOP_1_WIN 27 +#define MV64x60_CPU_SNOOP_2_WIN 28 +#define MV64x60_CPU_SNOOP_3_WIN 29 +#define MV64x60_PCI02MEM_REMAP_0_WIN 30 +#define MV64x60_PCI02MEM_REMAP_1_WIN 31 +#define MV64x60_PCI02MEM_REMAP_2_WIN 32 +#define MV64x60_PCI02MEM_REMAP_3_WIN 33 +#define MV64x60_PCI12MEM_REMAP_0_WIN 34 +#define MV64x60_PCI12MEM_REMAP_1_WIN 35 +#define MV64x60_PCI12MEM_REMAP_2_WIN 36 +#define MV64x60_PCI12MEM_REMAP_3_WIN 37 + +#define MV64x60_32BIT_WIN_COUNT 38 + +/* 64-bit Window table entry defines */ +#define MV64x60_CPU2PCI0_MEM_0_REMAP_WIN 0 +#define MV64x60_CPU2PCI0_MEM_1_REMAP_WIN 1 +#define MV64x60_CPU2PCI0_MEM_2_REMAP_WIN 2 +#define MV64x60_CPU2PCI0_MEM_3_REMAP_WIN 3 +#define MV64x60_CPU2PCI1_MEM_0_REMAP_WIN 4 +#define MV64x60_CPU2PCI1_MEM_1_REMAP_WIN 5 +#define MV64x60_CPU2PCI1_MEM_2_REMAP_WIN 6 +#define MV64x60_CPU2PCI1_MEM_3_REMAP_WIN 7 +#define MV64x60_PCI02MEM_ACC_CNTL_0_WIN 8 +#define MV64x60_PCI02MEM_ACC_CNTL_1_WIN 9 +#define MV64x60_PCI02MEM_ACC_CNTL_2_WIN 10 +#define MV64x60_PCI02MEM_ACC_CNTL_3_WIN 11 +#define MV64x60_PCI12MEM_ACC_CNTL_0_WIN 12 +#define MV64x60_PCI12MEM_ACC_CNTL_1_WIN 13 +#define MV64x60_PCI12MEM_ACC_CNTL_2_WIN 14 +#define MV64x60_PCI12MEM_ACC_CNTL_3_WIN 15 +#define MV64x60_PCI02MEM_SNOOP_0_WIN 16 +#define MV64x60_PCI02MEM_SNOOP_1_WIN 17 +#define MV64x60_PCI02MEM_SNOOP_2_WIN 18 +#define MV64x60_PCI02MEM_SNOOP_3_WIN 19 +#define MV64x60_PCI12MEM_SNOOP_0_WIN 20 +#define MV64x60_PCI12MEM_SNOOP_1_WIN 21 +#define MV64x60_PCI12MEM_SNOOP_2_WIN 22 +#define MV64x60_PCI12MEM_SNOOP_3_WIN 23 + +#define MV64x60_64BIT_WIN_COUNT 24 + + +/* + * Define a structure that's used to pass in config information to the + * core routines. + */ +typedef struct { + u32 cpu_base; + u32 pci_base_hi; + u32 pci_base_lo; + u32 size; + u32 swap; +} mv64x60_pci_window_t; + +typedef struct { + u8 enable_bus; /* allow access to this PCI bus? */ + u8 enumerate_bus; /* enumerate devices on this bus? */ + + mv64x60_pci_window_t pci_io; + mv64x60_pci_window_t pci_mem[3]; + + u32 acc_cntl_options[MV64x60_CPU2MEM_WINDOWS]; + u32 snoop_options[MV64x60_CPU2MEM_WINDOWS]; + u16 pci_cmd_bits; + u16 latency_timer; +} mv64x60_pci_info_t; + +typedef struct { + u32 phys_reg_base; + + u32 window_preserve_mask_32; + u32 window_preserve_mask_64; + + u32 base_irq; /* Starting irq # for this intr ctlr */ + int ((*map_irq)(struct pci_dev *, unsigned char, unsigned char)); + + u32 cpu_prot_options[MV64x60_CPU2MEM_WINDOWS]; + u32 cpu_snoop_options[MV64x60_CPU2MEM_WINDOWS]; + + mv64x60_pci_info_t pci_0; + mv64x60_pci_info_t pci_1; +} mv64x60_setup_info_t; + +/* + * Define the 'handle' struct that will be passed between the 64x60 core + * code and the platform-specific code that will use it. The handle + * will contain pointers to chip-specific routines & information. + */ +typedef struct { + u32 base_reg; + u32 size_reg; + u8 base_bits; + u8 size_bits; + u32 (*get_from_field)(u32 val, u32 num_bits); + u32 (*map_to_field)(u32 val, u32 num_bits); + u32 extra; +} mv64x60_32bit_window_t; + +typedef struct { + u32 base_hi_reg; + u32 base_lo_reg; + u32 size_reg; + u8 base_lo_bits; + u8 size_bits; + u32 (*get_from_field)(u32 val, u32 num_bits); + u32 (*map_to_field)(u32 val, u32 num_bits); + u32 extra; +} mv64x60_64bit_window_t; + +typedef struct mv64x60_handle mv64x60_handle_t; + +typedef struct { + u32 (*translate_size)(u32 base, u32 size, u32 num_bits); + u32 (*untranslate_size)(u32 base, u32 size, u32 num_bits); + void (*set_pci2mem_window)(struct pci_controller *hose, u32 window, + u32 base); + u32 (*is_enabled_32bit)(mv64x60_handle_t *bh, u32 window); + void (*enable_window_32bit)(mv64x60_handle_t *bh, u32 window); + void (*disable_window_32bit)(mv64x60_handle_t *bh, u32 window); + void (*enable_window_64bit)(mv64x60_handle_t *bh, u32 window); + void (*disable_window_64bit)(mv64x60_handle_t *bh, u32 window); + void (*disable_all_windows)(mv64x60_handle_t *bh, + mv64x60_setup_info_t *si); + void (*chip_specific_init)(mv64x60_handle_t *bh, + mv64x60_setup_info_t *si); + + mv64x60_32bit_window_t *window_tab_32bit; + mv64x60_64bit_window_t *window_tab_64bit; +} mv64x60_chip_info_t; + +struct mv64x60_handle { + u32 type; /* type of bridge */ + u32 v_base; /* virtual base addr of bridge regs */ + u32 p_base; /* physical base addr of bridge regs */ + u32 base_irq; /* Base irq # for intrs on this intr cltr */ + + u32 io_base_a; /* vaddr of pci 0's I/O space */ + u32 io_base_b; /* vaddr of pci 1's I/O space */ + + struct pci_controller *hose_a; + struct pci_controller *hose_b; + + mv64x60_chip_info_t *ci; /* chip/bridge-specific info */ +}; + + +/* Define I/O routines for accessing registers on the 64x60 bridge. */ +extern inline void +mv64x60_write(mv64x60_handle_t *bh, u32 offset, u32 val) { + out_le32((volatile u32 *)(bh->v_base + offset), val); +} + +extern inline u32 +mv64x60_read(mv64x60_handle_t *bh, u32 offset) { + return in_le32((volatile u32 *)(bh->v_base + offset)); +} + +extern inline void +mv64x60_modify(mv64x60_handle_t *bh, u32 offs, u32 data, u32 mask) +{ + uint32_t reg; + unsigned long flags; + + spin_lock_irqsave(&mv64x60_rmw_lock, flags); + reg = mv64x60_read(bh, offs) & (~mask); /* zero any bits we care about*/ + reg |= data & mask; /* set bits from the data */ + mv64x60_write(bh, offs, reg); + spin_unlock_irqrestore(&mv64x60_rmw_lock, flags); +} + +#define mv64x60_set_bits(bh, offs, bits) mv64x60_modify(bh, offs, ~0, bits) +#define mv64x60_clr_bits(bh, offs, bits) mv64x60_modify(bh, offs, 0, bits) + + +/* Externally visible function prototypes */ +int mv64x60_init(mv64x60_handle_t *bh, mv64x60_setup_info_t *si); +u32 mv64x60_get_mem_size(u32 bridge_base, u32 chip_type); +void mv64x60_get_32bit_window(mv64x60_handle_t *bh, u32 window, + u32 *base, u32 *size); +void mv64x60_set_32bit_window(mv64x60_handle_t *bh, u32 window, u32 base, + u32 size, u32 other_bits); +void mv64x60_get_64bit_window(mv64x60_handle_t *bh, u32 window, u32 *base_hi, + u32 *base_lo, u32 *size); +void mv64x60_set_64bit_window(mv64x60_handle_t *bh, u32 window, u32 base_hi, + u32 base_lo, u32 size, u32 other_bits); + + +void gt64260_init_irq(void); +int gt64260_get_irq(struct pt_regs *regs); + +/* + * OCP Related Definitions + */ +typedef struct { + u8 mirror_regs; + u8 cache_mgmt; + u8 max_idle; + int default_baud; + int default_bits; + int default_parity; + int default_flow; + u32 chr_1_val; + u32 chr_2_val; + u32 chr_10_val; + u32 mpcr_val; + u32 mrr_val; + u32 rcrr_val; + u32 tcrr_val; + u32 intr_mask_val; + u32 bcr_val; + u32 sdma_irq; + u8 brg_can_tune; + u8 brg_clk_src; + u32 brg_clk_freq; +} mv64x60_ocp_mpsc_data_t; + +#define MV64x60_OCP_SYSFS_MPSC_DATA() \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, mirror_regs) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, cache_mgmt) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, max_idle) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, default_baud) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, default_bits) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%c\n", mpsc, default_parity) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%c\n", mpsc, default_flow) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, chr_1_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, chr_2_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, chr_10_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, mpcr_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, mrr_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, rcrr_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, tcrr_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, intr_mask_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "0x%x\n", mpsc, bcr_val) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, sdma_irq) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, brg_can_tune) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, brg_clk_src) \ +OCP_SYSFS_ADDTL(mv64x60_ocp_mpsc_data_t, "%d\n", mpsc, brg_clk_freq) \ + \ +void \ +mv64x60_ocp_show_mpsc(struct device *dev) \ +{ \ + device_create_file(dev, &dev_attr_mpsc_mirror_regs); \ + device_create_file(dev, &dev_attr_mpsc_cache_mgmt); \ + device_create_file(dev, &dev_attr_mpsc_max_idle); \ + device_create_file(dev, &dev_attr_mpsc_default_baud); \ + device_create_file(dev, &dev_attr_mpsc_default_bits); \ + device_create_file(dev, &dev_attr_mpsc_default_parity); \ + device_create_file(dev, &dev_attr_mpsc_default_flow); \ + device_create_file(dev, &dev_attr_mpsc_chr_1_val); \ + device_create_file(dev, &dev_attr_mpsc_chr_2_val); \ + device_create_file(dev, &dev_attr_mpsc_chr_10_val); \ + device_create_file(dev, &dev_attr_mpsc_mpcr_val); \ + device_create_file(dev, &dev_attr_mpsc_mrr_val); \ + device_create_file(dev, &dev_attr_mpsc_rcrr_val); \ + device_create_file(dev, &dev_attr_mpsc_tcrr_val); \ + device_create_file(dev, &dev_attr_mpsc_intr_mask_val); \ + device_create_file(dev, &dev_attr_mpsc_bcr_val); \ + device_create_file(dev, &dev_attr_mpsc_sdma_irq); \ + device_create_file(dev, &dev_attr_mpsc_brg_can_tune); \ + device_create_file(dev, &dev_attr_mpsc_brg_clk_src); \ + device_create_file(dev, &dev_attr_mpsc_brg_clk_freq); \ +} + +#endif /* __ASMPPC_MV64x60_H */ diff --git a/include/asm-ppc/mv64x60_defs.h b/include/asm-ppc/mv64x60_defs.h new file mode 100644 index 000000000..6f7899da2 --- /dev/null +++ b/include/asm-ppc/mv64x60_defs.h @@ -0,0 +1,996 @@ +/* + * include/asm-ppc/gt64260_defs.h + * + * Register definitions for the Marvell/Galileo GT64260, MV64360, etc. + * host bridges. + * + * Author: Mark A. Greer + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef __ASMPPC_MV64x60_DEFS_H +#define __ASMPPC_MV64x60_DEFS_H + +/* + * Define the Marvell bridges that are supported + */ +#define MV64x60_TYPE_INVALID 0 +#define MV64x60_TYPE_GT64260A 1 +#define MV64x60_TYPE_GT64260B 2 +#define MV64x60_TYPE_MV64360 3 +#define MV64x60_TYPE_MV64361 4 +#define MV64x60_TYPE_MV64362 5 +#define MV64x60_TYPE_MV64460 6 + + +/* Revisions of each supported chip */ +#define GT64260_REV_A 0x10 +#define GT64260_REV_B 0x20 +#define MV64360 0x01 +#define MV64460 0x01 + +/* Minimum window size supported by 64260 is 1MB */ +#define GT64260_WINDOW_SIZE_MIN 0x00100000 +#define MV64360_WINDOW_SIZE_MIN 0x00010000 + +/* IRQ's for embedded controllers */ +#define MV64x60_IRQ_DEV 1 +#define MV64x60_IRQ_CPU_ERR 3 +#define MV64x60_IRQ_TIMER_0_1 8 +#define MV64x60_IRQ_TIMER_2_3 9 +#define MV64x60_IRQ_TIMER_4_5 10 +#define MV64x60_IRQ_TIMER_6_7 11 +#define MV64x60_IRQ_ETH_0 32 +#define MV64x60_IRQ_ETH_1 33 +#define MV64x60_IRQ_ETH_2 34 +#define MV64x60_IRQ_SDMA_0 36 +#define MV64x60_IRQ_I2C 37 +#define MV64x60_IRQ_SDMA_1 38 +#define MV64x60_IRQ_BRG 39 +#define MV64x60_IRQ_MPSC_0 40 +#define MV64x60_IRQ_MPSC_1 42 +#define MV64x60_IRQ_COMM 43 + +#define MV64360_IRQ_PCI0 12 +#define MV64360_IRQ_SRAM_PAR_ERR 13 +#define MV64360_IRQ_PCI1 16 + +/* Offsets for register blocks */ +#define MV64x60_MPSC_0_OFFSET 0x8000 +#define MV64x60_MPSC_1_OFFSET 0x9000 +#define MV64x60_MPSC_ROUTING_OFFSET 0xb400 +#define MV64x60_SDMA_0_OFFSET 0x4000 +#define MV64x60_SDMA_1_OFFSET 0x6000 +#define MV64x60_SDMA_INTR_OFFSET 0xb800 +#define MV64x60_BRG_0_OFFSET 0xb200 +#define MV64x60_BRG_1_OFFSET 0xb208 + +/* + ***************************************************************************** + * + * CPU Interface Registers + * + ***************************************************************************** + */ + +/* CPU physical address of 64260's registers */ +#define MV64x60_INTERNAL_SPACE_DECODE 0x0068 +#define MV64x60_INTERNAL_SPACE_SIZE 0x10000 +#define MV64x60_INTERNAL_SPACE_DEFAULT_ADDR 0x14000000 + +#define MV64360_CPU_BAR_ENABLE 0x0278 + +/* CPU Memory Controller Window Registers (4 windows) */ +#define MV64x60_CPU2MEM_WINDOWS 4 + +#define MV64x60_CPU2MEM_0_BASE 0x0008 +#define MV64x60_CPU2MEM_0_SIZE 0x0010 +#define MV64x60_CPU2MEM_1_BASE 0x0208 +#define MV64x60_CPU2MEM_1_SIZE 0x0210 +#define MV64x60_CPU2MEM_2_BASE 0x0018 +#define MV64x60_CPU2MEM_2_SIZE 0x0020 +#define MV64x60_CPU2MEM_3_BASE 0x0218 +#define MV64x60_CPU2MEM_3_SIZE 0x0220 + +/* CPU Device Controller Window Registers (4 windows) */ +#define MV64x60_CPU2DEV_CS_WINDOWS 4 + +#define MV64x60_CPU2DEV_0_BASE 0x0028 +#define MV64x60_CPU2DEV_0_SIZE 0x0030 +#define MV64x60_CPU2DEV_1_BASE 0x0228 +#define MV64x60_CPU2DEV_1_SIZE 0x0230 +#define MV64x60_CPU2DEV_2_BASE 0x0248 +#define MV64x60_CPU2DEV_2_SIZE 0x0250 +#define MV64x60_CPU2DEV_3_BASE 0x0038 +#define MV64x60_CPU2DEV_3_SIZE 0x0040 + +#define MV64x60_CPU2BOOT_0_BASE 0x0238 +#define MV64x60_CPU2BOOT_0_SIZE 0x0240 + +/* CPU Windows to PCI space (2 PCI buses each w/ 1 I/O & 4 MEM windows) */ +#define MV64x60_PCI_BUSES 2 +#define MV64x60_PCI_IO_WINDOWS_PER_BUS 1 +#define MV64x60_PCI_MEM_WINDOWS_PER_BUS 4 + +#define MV64x60_CPU2PCI_SWAP_BYTE 0x00000000 +#define MV64x60_CPU2PCI_SWAP_NONE 0x01000000 +#define MV64x60_CPU2PCI_SWAP_BYTE_WORD 0x02000000 +#define MV64x60_CPU2PCI_SWAP_WORD 0x03000000 + +#define MV64x60_CPU2PCI_MEM_REQ64 (1<<27) + +#define MV64x60_CPU2PCI0_IO_BASE 0x0048 +#define MV64x60_CPU2PCI0_IO_SIZE 0x0050 +#define MV64x60_CPU2PCI0_MEM_0_BASE 0x0058 +#define MV64x60_CPU2PCI0_MEM_0_SIZE 0x0060 +#define MV64x60_CPU2PCI0_MEM_1_BASE 0x0080 +#define MV64x60_CPU2PCI0_MEM_1_SIZE 0x0088 +#define MV64x60_CPU2PCI0_MEM_2_BASE 0x0258 +#define MV64x60_CPU2PCI0_MEM_2_SIZE 0x0260 +#define MV64x60_CPU2PCI0_MEM_3_BASE 0x0280 +#define MV64x60_CPU2PCI0_MEM_3_SIZE 0x0288 + +#define MV64x60_CPU2PCI0_IO_REMAP 0x00f0 +#define MV64x60_CPU2PCI0_MEM_0_REMAP_LO 0x00f8 +#define MV64x60_CPU2PCI0_MEM_0_REMAP_HI 0x0320 +#define MV64x60_CPU2PCI0_MEM_1_REMAP_LO 0x0100 +#define MV64x60_CPU2PCI0_MEM_1_REMAP_HI 0x0328 +#define MV64x60_CPU2PCI0_MEM_2_REMAP_LO 0x02f8 +#define MV64x60_CPU2PCI0_MEM_2_REMAP_HI 0x0330 +#define MV64x60_CPU2PCI0_MEM_3_REMAP_LO 0x0300 +#define MV64x60_CPU2PCI0_MEM_3_REMAP_HI 0x0338 + +#define MV64x60_CPU2PCI1_IO_BASE 0x0090 +#define MV64x60_CPU2PCI1_IO_SIZE 0x0098 +#define MV64x60_CPU2PCI1_MEM_0_BASE 0x00a0 +#define MV64x60_CPU2PCI1_MEM_0_SIZE 0x00a8 +#define MV64x60_CPU2PCI1_MEM_1_BASE 0x00b0 +#define MV64x60_CPU2PCI1_MEM_1_SIZE 0x00b8 +#define MV64x60_CPU2PCI1_MEM_2_BASE 0x02a0 +#define MV64x60_CPU2PCI1_MEM_2_SIZE 0x02a8 +#define MV64x60_CPU2PCI1_MEM_3_BASE 0x02b0 +#define MV64x60_CPU2PCI1_MEM_3_SIZE 0x02b8 + +#define MV64360_CPU2SRAM_BASE 0x0268 + +#define MV64x60_CPU2PCI1_IO_REMAP 0x0108 +#define MV64x60_CPU2PCI1_MEM_0_REMAP_LO 0x0110 +#define MV64x60_CPU2PCI1_MEM_0_REMAP_HI 0x0340 +#define MV64x60_CPU2PCI1_MEM_1_REMAP_LO 0x0118 +#define MV64x60_CPU2PCI1_MEM_1_REMAP_HI 0x0348 +#define MV64x60_CPU2PCI1_MEM_2_REMAP_LO 0x0310 +#define MV64x60_CPU2PCI1_MEM_2_REMAP_HI 0x0350 +#define MV64x60_CPU2PCI1_MEM_3_REMAP_LO 0x0318 +#define MV64x60_CPU2PCI1_MEM_3_REMAP_HI 0x0358 + +/* CPU Control Registers */ +#define MV64x60_CPU_CONFIG 0x0000 +#define MV64x60_CPU_MODE 0x0120 +#define MV64x60_CPU_MASTER_CNTL 0x0160 +#define MV64x60_CPU_XBAR_CNTL_LO 0x0150 +#define MV64x60_CPU_XBAR_CNTL_HI 0x0158 +#define MV64x60_CPU_XBAR_TO 0x0168 + +#define GT64260_CPU_RR_XBAR_CNTL_LO 0x0170 +#define GT64260_CPU_RR_XBAR_CNTL_HI 0x0178 + +#define MV64360_CPU_PADS_CALIBRATION 0x03b4 +#define MV64360_CPU_RESET_SAMPLE_LO 0x03c4 +#define MV64360_CPU_RESET_SAMPLE_HI 0x03d4 + +/* SMP Register Map */ +#define MV64360_WHO_AM_I 0x0200 +#define MV64360_CPU0_DOORBELL 0x0214 +#define MV64360_CPU0_DOORBELL_CLR 0x021c +#define MV64360_CPU0_DOORBELL_MASK 0x0234 +#define MV64360_CPU1_DOORBELL 0x0224 +#define MV64360_CPU1_DOORBELL_CLR 0x022c +#define MV64360_CPU1_DOORBELL_MASK 0x023c +#define MV64360_CPUx_DOORBELL(x) (0x0214 + ((x)*0x10)) +#define MV64360_CPUx_DOORBELL_CLR(x) (0x021c + ((x)*0x10)) +#define MV64360_CPUx_DOORBELL_MASK(x) (0x0234 + ((x)*0x08)) +#define MV64360_SEMAPHORE_0 0x0244 +#define MV64360_SEMAPHORE_1 0x024c +#define MV64360_SEMAPHORE_2 0x0254 +#define MV64360_SEMAPHORE_3 0x025c +#define MV64360_SEMAPHORE_4 0x0264 +#define MV64360_SEMAPHORE_5 0x026c +#define MV64360_SEMAPHORE_6 0x0274 +#define MV64360_SEMAPHORE_7 0x027c + +/* CPU Sync Barrier Registers */ +#define GT64260_CPU_SYNC_BARRIER_PCI0 0x00c0 +#define GT64260_CPU_SYNC_BARRIER_PCI1 0x00c8 + +#define MV64360_CPU0_SYNC_BARRIER_TRIG 0x00c0 +#define MV64360_CPU0_SYNC_BARRIER_VIRT 0x00c8 +#define MV64360_CPU1_SYNC_BARRIER_TRIG 0x00d0 +#define MV64360_CPU1_SYNC_BARRIER_VIRT 0x00d8 + +/* CPU Deadlock and Ordering registers (Rev B part only) */ +#define GT64260_CPU_DEADLOCK_ORDERING 0x02d0 +#define GT64260_CPU_WB_PRIORITY_BUFFER_DEPTH 0x02d8 +#define GT64260_CPU_COUNTERS_SYNC_BARRIER_ATTRIBUTE 0x02e0 + +/* CPU Access Protection Registers (gt64260 realy has 8 but don't need) */ +#define MV64x260_CPU_PROT_WINDOWS 4 + +#define GT64260_CPU_PROT_ACCPROTECT (1<<16) +#define GT64260_CPU_PROT_WRPROTECT (1<<17) +#define GT64260_CPU_PROT_CACHEPROTECT (1<<18) + +#define MV64360_CPU_PROT_ACCPROTECT (1<<20) +#define MV64360_CPU_PROT_WRPROTECT (1<<21) +#define MV64360_CPU_PROT_CACHEPROTECT (1<<22) +#define MV64360_CPU_PROT_WIN_ENABLE (1<<31) + +#define MV64x60_CPU_PROT_BASE_0 0x0180 +#define MV64x60_CPU_PROT_SIZE_0 0x0188 +#define MV64x60_CPU_PROT_BASE_1 0x0190 +#define MV64x60_CPU_PROT_SIZE_1 0x0198 +#define MV64x60_CPU_PROT_BASE_2 0x01a0 +#define MV64x60_CPU_PROT_SIZE_2 0x01a8 +#define MV64x60_CPU_PROT_BASE_3 0x01b0 +#define MV64x60_CPU_PROT_SIZE_3 0x01b8 + +#define GT64260_CPU_PROT_BASE_4 0x01c0 +#define GT64260_CPU_PROT_SIZE_4 0x01c8 +#define GT64260_CPU_PROT_BASE_5 0x01d0 +#define GT64260_CPU_PROT_SIZE_5 0x01d8 +#define GT64260_CPU_PROT_BASE_6 0x01e0 +#define GT64260_CPU_PROT_SIZE_6 0x01e8 +#define GT64260_CPU_PROT_BASE_7 0x01f0 +#define GT64260_CPU_PROT_SIZE_7 0x01f8 + +/* CPU Snoop Control Registers (64260 only) */ +#define GT64260_CPU_SNOOP_WINDOWS 4 + +#define GT64260_CPU_SNOOP_NONE 0x00000000 +#define GT64260_CPU_SNOOP_WT 0x00010000 +#define GT64260_CPU_SNOOP_WB 0x00020000 +#define GT64260_CPU_SNOOP_MASK 0x00030000 +#define GT64260_CPU_SNOOP_ALL_BITS GT64260_CPU_SNOOP_MASK + +#define GT64260_CPU_SNOOP_BASE_0 0x0380 +#define GT64260_CPU_SNOOP_SIZE_0 0x0388 +#define GT64260_CPU_SNOOP_BASE_1 0x0390 +#define GT64260_CPU_SNOOP_SIZE_1 0x0398 +#define GT64260_CPU_SNOOP_BASE_2 0x03a0 +#define GT64260_CPU_SNOOP_SIZE_2 0x03a8 +#define GT64260_CPU_SNOOP_BASE_3 0x03b0 +#define GT64260_CPU_SNOOP_SIZE_3 0x03b8 + +/* CPU Error Report Registers */ +#define MV64x60_CPU_ERR_ADDR_LO 0x0070 +#define MV64x60_CPU_ERR_ADDR_HI 0x0078 +#define MV64x60_CPU_ERR_DATA_LO 0x0128 +#define MV64x60_CPU_ERR_DATA_HI 0x0130 +#define MV64x60_CPU_ERR_PARITY 0x0138 +#define MV64x60_CPU_ERR_CAUSE 0x0140 +#define MV64x60_CPU_ERR_MASK 0x0148 + +/* + ***************************************************************************** + * + * SRAM Cotnroller Registers + * + ***************************************************************************** + */ + +#define MV64360_SRAM_CONFIG 0x0380 +#define MV64360_SRAM_TEST_MODE 0x03f4 +#define MV64360_SRAM_ERR_CAUSE 0x0388 +#define MV64360_SRAM_ERR_ADDR_LO 0x0390 +#define MV64360_SRAM_ERR_ADDR_HI 0x03f8 +#define MV64360_SRAM_ERR_DATA_LO 0x0398 +#define MV64360_SRAM_ERR_DATA_HI 0x03a0 +#define MV64360_SRAM_ERR_PARITY 0x03a8 + + +/* + ***************************************************************************** + * + * SDRAM Cotnroller Registers + * + ***************************************************************************** + */ + +/* SDRAM Config Registers (64260) */ +#define GT64260_SDRAM_CONFIG 0x0448 + +/* SDRAM Error Report Registers (64260) */ +#define GT64260_SDRAM_ERR_DATA_LO 0x0484 +#define GT64260_SDRAM_ERR_DATA_HI 0x0480 +#define GT64260_SDRAM_ERR_ADDR 0x0490 +#define GT64260_SDRAM_ERR_ECC_RCVD 0x0488 +#define GT64260_SDRAM_ERR_ECC_CALC 0x048c +#define GT64260_SDRAM_ERR_ECC_CNTL 0x0494 +#define GT64260_SDRAM_ERR_ECC_ERR_CNT 0x0498 + +/* SDRAM Config Registers (64360) */ +#define MV64360_SDRAM_CONFIG 0x1400 + +/* SDRAM Error Report Registers (64360) */ +#define MV64360_SDRAM_ERR_DATA_LO 0x1444 +#define MV64360_SDRAM_ERR_DATA_HI 0x1440 +#define MV64360_SDRAM_ERR_ADDR 0x1450 +#define MV64360_SDRAM_ERR_ECC_RCVD 0x1448 +#define MV64360_SDRAM_ERR_ECC_CALC 0x144c +#define MV64360_SDRAM_ERR_ECC_CNTL 0x1454 +#define MV64360_SDRAM_ERR_ECC_ERR_CNT 0x1458 + + +/* + ***************************************************************************** + * + * Device/BOOT Cotnroller Registers + * + ***************************************************************************** + */ + +/* Device Control Registers */ +#define MV64x60_DEV_BANK_PARAMS_0 0x045c +#define MV64x60_DEV_BANK_PARAMS_1 0x0460 +#define MV64x60_DEV_BANK_PARAMS_2 0x0464 +#define MV64x60_DEV_BANK_PARAMS_3 0x0468 +#define MV64x60_DEV_BOOT_PARAMS 0x046c +#define MV64x60_DEV_IF_CNTL 0x04c0 +#define MV64x60_DEV_IF_XBAR_CNTL_LO 0x04c8 +#define MV64x60_DEV_IF_XBAR_CNTL_HI 0x04cc +#define MV64x60_DEV_IF_XBAR_CNTL_TO 0x04c4 + +/* Device Interrupt Registers */ +#define MV64x60_DEV_INTR_CAUSE 0x04d0 +#define MV64x60_DEV_INTR_MASK 0x04d4 +#define MV64x60_DEV_INTR_ERR_ADDR 0x04d8 + +#define MV64360_DEV_INTR_ERR_DATA 0x04dc +#define MV64360_DEV_INTR_ERR_PAR 0x04e0 + + +/* + ***************************************************************************** + * + * PCI Bridge Interface Registers + * + ***************************************************************************** + */ + +/* PCI Configuration Access Registers */ +#define MV64x60_PCI0_CONFIG_ADDR 0x0cf8 +#define MV64x60_PCI0_CONFIG_DATA 0x0cfc +#define MV64x60_PCI0_IACK 0x0c34 + +#define MV64x60_PCI1_CONFIG_ADDR 0x0c78 +#define MV64x60_PCI1_CONFIG_DATA 0x0c7c +#define MV64x60_PCI1_IACK 0x0cb4 + +/* PCI Control Registers */ +#define MV64x60_PCI0_CMD 0x0c00 +#define MV64x60_PCI0_MODE 0x0d00 +#define MV64x60_PCI0_TO_RETRY 0x0c04 +#define MV64x60_PCI0_RD_BUF_DISCARD_TIMER 0x0d04 +#define MV64x60_PCI0_MSI_TRIGGER_TIMER 0x0c38 +#define MV64x60_PCI0_ARBITER_CNTL 0x1d00 +#define MV64x60_PCI0_XBAR_CNTL_LO 0x1d08 +#define MV64x60_PCI0_XBAR_CNTL_HI 0x1d0c +#define MV64x60_PCI0_XBAR_CNTL_TO 0x1d04 +#define MV64x60_PCI0_RD_RESP_XBAR_CNTL_LO 0x1d18 +#define MV64x60_PCI0_RD_RESP_XBAR_CNTL_HI 0x1d1c +#define MV64x60_PCI0_SYNC_BARRIER 0x1d10 +#define MV64x60_PCI0_P2P_CONFIG 0x1d14 +#define MV64x60_PCI0_INTR_MASK + +#define GT64260_PCI0_P2P_SWAP_CNTL 0x1d54 + +#define MV64x60_PCI1_CMD 0x0c80 +#define MV64x60_PCI1_MODE 0x0d80 +#define MV64x60_PCI1_TO_RETRY 0x0c84 +#define MV64x60_PCI1_RD_BUF_DISCARD_TIMER 0x0d84 +#define MV64x60_PCI1_MSI_TRIGGER_TIMER 0x0cb8 +#define MV64x60_PCI1_ARBITER_CNTL 0x1d80 +#define MV64x60_PCI1_XBAR_CNTL_LO 0x1d88 +#define MV64x60_PCI1_XBAR_CNTL_HI 0x1d8c +#define MV64x60_PCI1_XBAR_CNTL_TO 0x1d84 +#define MV64x60_PCI1_RD_RESP_XBAR_CNTL_LO 0x1d98 +#define MV64x60_PCI1_RD_RESP_XBAR_CNTL_HI 0x1d9c +#define MV64x60_PCI1_SYNC_BARRIER 0x1d90 +#define MV64x60_PCI1_P2P_CONFIG 0x1d94 + +#define GT64260_PCI1_P2P_SWAP_CNTL 0x1dd4 + +/* PCI Access Control Regions Registers */ +#define GT64260_PCI_ACC_CNTL_PREFETCHEN (1<<12) +#define GT64260_PCI_ACC_CNTL_DREADEN (1<<13) +#define GT64260_PCI_ACC_CNTL_RDPREFETCH (1<<16) +#define GT64260_PCI_ACC_CNTL_RDLINEPREFETCH (1<<17) +#define GT64260_PCI_ACC_CNTL_RDMULPREFETCH (1<<18) +#define GT64260_PCI_ACC_CNTL_MBURST_32_BTYES 0x00000000 +#define GT64260_PCI_ACC_CNTL_MBURST_64_BYTES 0x00100000 +#define GT64260_PCI_ACC_CNTL_MBURST_128_BYTES 0x00200000 +#define GT64260_PCI_ACC_CNTL_MBURST_MASK 0x00300000 +#define GT64260_PCI_ACC_CNTL_SWAP_BYTE 0x00000000 +#define GT64260_PCI_ACC_CNTL_SWAP_NONE 0x01000000 +#define GT64260_PCI_ACC_CNTL_SWAP_BYTE_WORD 0x02000000 +#define GT64260_PCI_ACC_CNTL_SWAP_WORD 0x03000000 +#define GT64260_PCI_ACC_CNTL_SWAP_MASK 0x03000000 +#define GT64260_PCI_ACC_CNTL_ACCPROT (1<<28) +#define GT64260_PCI_ACC_CNTL_WRPROT (1<<29) + +#define GT64260_PCI_ACC_CNTL_ALL_BITS (GT64260_PCI_ACC_CNTL_PREFETCHEN | \ + GT64260_PCI_ACC_CNTL_DREADEN | \ + GT64260_PCI_ACC_CNTL_RDPREFETCH | \ + GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |\ + GT64260_PCI_ACC_CNTL_RDMULPREFETCH | \ + GT64260_PCI_ACC_CNTL_MBURST_MASK | \ + GT64260_PCI_ACC_CNTL_SWAP_MASK | \ + GT64260_PCI_ACC_CNTL_ACCPROT| \ + GT64260_PCI_ACC_CNTL_WRPROT) + +#define MV64360_PCI_ACC_CNTL_ENABLE (1<<0) +#define MV64360_PCI_ACC_CNTL_REQ64 (1<<1) +#define MV64360_PCI_ACC_CNTL_SNOOP_NONE 0x00000000 +#define MV64360_PCI_ACC_CNTL_SNOOP_WT 0x00000004 +#define MV64360_PCI_ACC_CNTL_SNOOP_WB 0x00000008 +#define MV64360_PCI_ACC_CNTL_SNOOP_MASK 0x0000000c +#define MV64360_PCI_ACC_CNTL_ACCPROT (1<<4) +#define MV64360_PCI_ACC_CNTL_WRPROT (1<<5) +#define MV64360_PCI_ACC_CNTL_SWAP_BYTE 0x00000000 +#define MV64360_PCI_ACC_CNTL_SWAP_NONE 0x00000040 +#define MV64360_PCI_ACC_CNTL_SWAP_BYTE_WORD 0x00000080 +#define MV64360_PCI_ACC_CNTL_SWAP_WORD 0x000000c0 +#define MV64360_PCI_ACC_CNTL_SWAP_MASK 0x000000c0 +#define MV64360_PCI_ACC_CNTL_MBURST_32_BYTES 0x00000000 +#define MV64360_PCI_ACC_CNTL_MBURST_64_BYTES 0x00000100 +#define MV64360_PCI_ACC_CNTL_MBURST_128_BYTES 0x00000200 +#define MV64360_PCI_ACC_CNTL_MBURST_MASK 0x00000300 +#define MV64360_PCI_ACC_CNTL_RDSIZE_32_BYTES 0x00000000 +#define MV64360_PCI_ACC_CNTL_RDSIZE_64_BYTES 0x00000400 +#define MV64360_PCI_ACC_CNTL_RDSIZE_128_BYTES 0x00000800 +#define MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES 0x00000c00 +#define MV64360_PCI_ACC_CNTL_RDSIZE_MASK 0x00000c00 + +#define MV64360_PCI_ACC_CNTL_ALL_BITS (MV64360_PCI_ACC_CNTL_ENABLE | \ + MV64360_PCI_ACC_CNTL_REQ64 | \ + MV64360_PCI_ACC_CNTL_SNOOP_MASK | \ + MV64360_PCI_ACC_CNTL_ACCPROT | \ + MV64360_PCI_ACC_CNTL_WRPROT | \ + MV64360_PCI_ACC_CNTL_SWAP_MASK | \ + MV64360_PCI_ACC_CNTL_MBURST_MASK | \ + MV64360_PCI_ACC_CNTL_RDSIZE_MASK) + +#define MV64x60_PCI0_ACC_CNTL_0_BASE_LO 0x1e00 +#define MV64x60_PCI0_ACC_CNTL_0_BASE_HI 0x1e04 +#define MV64x60_PCI0_ACC_CNTL_0_SIZE 0x1e08 +#define MV64x60_PCI0_ACC_CNTL_1_BASE_LO 0x1e10 +#define MV64x60_PCI0_ACC_CNTL_1_BASE_HI 0x1e14 +#define MV64x60_PCI0_ACC_CNTL_1_SIZE 0x1e18 +#define MV64x60_PCI0_ACC_CNTL_2_BASE_LO 0x1e20 +#define MV64x60_PCI0_ACC_CNTL_2_BASE_HI 0x1e24 +#define MV64x60_PCI0_ACC_CNTL_2_SIZE 0x1e28 +#define MV64x60_PCI0_ACC_CNTL_3_BASE_LO 0x1e30 +#define MV64x60_PCI0_ACC_CNTL_3_BASE_HI 0x1e34 +#define MV64x60_PCI0_ACC_CNTL_3_SIZE 0x1e38 +#define MV64x60_PCI0_ACC_CNTL_4_BASE_LO 0x1e40 +#define MV64x60_PCI0_ACC_CNTL_4_BASE_HI 0x1e44 +#define MV64x60_PCI0_ACC_CNTL_4_SIZE 0x1e48 +#define MV64x60_PCI0_ACC_CNTL_5_BASE_LO 0x1e50 +#define MV64x60_PCI0_ACC_CNTL_5_BASE_HI 0x1e54 +#define MV64x60_PCI0_ACC_CNTL_5_SIZE 0x1e58 + +#define GT64260_PCI0_ACC_CNTL_6_BASE_LO 0x1e60 +#define GT64260_PCI0_ACC_CNTL_6_BASE_HI 0x1e64 +#define GT64260_PCI0_ACC_CNTL_6_SIZE 0x1e68 +#define GT64260_PCI0_ACC_CNTL_7_BASE_LO 0x1e70 +#define GT64260_PCI0_ACC_CNTL_7_BASE_HI 0x1e74 +#define GT64260_PCI0_ACC_CNTL_7_SIZE 0x1e78 + +#define MV64x60_PCI1_ACC_CNTL_0_BASE_LO 0x1e80 +#define MV64x60_PCI1_ACC_CNTL_0_BASE_HI 0x1e84 +#define MV64x60_PCI1_ACC_CNTL_0_SIZE 0x1e88 +#define MV64x60_PCI1_ACC_CNTL_1_BASE_LO 0x1e90 +#define MV64x60_PCI1_ACC_CNTL_1_BASE_HI 0x1e94 +#define MV64x60_PCI1_ACC_CNTL_1_SIZE 0x1e98 +#define MV64x60_PCI1_ACC_CNTL_2_BASE_LO 0x1ea0 +#define MV64x60_PCI1_ACC_CNTL_2_BASE_HI 0x1ea4 +#define MV64x60_PCI1_ACC_CNTL_2_SIZE 0x1ea8 +#define MV64x60_PCI1_ACC_CNTL_3_BASE_LO 0x1eb0 +#define MV64x60_PCI1_ACC_CNTL_3_BASE_HI 0x1eb4 +#define MV64x60_PCI1_ACC_CNTL_3_SIZE 0x1eb8 +#define MV64x60_PCI1_ACC_CNTL_4_BASE_LO 0x1ec0 +#define MV64x60_PCI1_ACC_CNTL_4_BASE_HI 0x1ec4 +#define MV64x60_PCI1_ACC_CNTL_4_SIZE 0x1ec8 +#define MV64x60_PCI1_ACC_CNTL_5_BASE_LO 0x1ed0 +#define MV64x60_PCI1_ACC_CNTL_5_BASE_HI 0x1ed4 +#define MV64x60_PCI1_ACC_CNTL_5_SIZE 0x1ed8 + +#define GT64260_PCI1_ACC_CNTL_6_BASE_LO 0x1ee0 +#define GT64260_PCI1_ACC_CNTL_6_BASE_HI 0x1ee4 +#define GT64260_PCI1_ACC_CNTL_6_SIZE 0x1ee8 +#define GT64260_PCI1_ACC_CNTL_7_BASE_LO 0x1ef0 +#define GT64260_PCI1_ACC_CNTL_7_BASE_HI 0x1ef4 +#define GT64260_PCI1_ACC_CNTL_7_SIZE 0x1ef8 + +/* PCI Snoop Control Registers (64260 only) */ +#define GT64260_PCI_SNOOP_NONE 0x00000000 +#define GT64260_PCI_SNOOP_WT 0x00001000 +#define GT64260_PCI_SNOOP_WB 0x00002000 + +#define GT64260_PCI0_SNOOP_0_BASE_LO 0x1f00 +#define GT64260_PCI0_SNOOP_0_BASE_HI 0x1f04 +#define GT64260_PCI0_SNOOP_0_SIZE 0x1f08 +#define GT64260_PCI0_SNOOP_1_BASE_LO 0x1f10 +#define GT64260_PCI0_SNOOP_1_BASE_HI 0x1f14 +#define GT64260_PCI0_SNOOP_1_SIZE 0x1f18 +#define GT64260_PCI0_SNOOP_2_BASE_LO 0x1f20 +#define GT64260_PCI0_SNOOP_2_BASE_HI 0x1f24 +#define GT64260_PCI0_SNOOP_2_SIZE 0x1f28 +#define GT64260_PCI0_SNOOP_3_BASE_LO 0x1f30 +#define GT64260_PCI0_SNOOP_3_BASE_HI 0x1f34 +#define GT64260_PCI0_SNOOP_3_SIZE 0x1f38 + +#define GT64260_PCI1_SNOOP_0_BASE_LO 0x1f80 +#define GT64260_PCI1_SNOOP_0_BASE_HI 0x1f84 +#define GT64260_PCI1_SNOOP_0_SIZE 0x1f88 +#define GT64260_PCI1_SNOOP_1_BASE_LO 0x1f90 +#define GT64260_PCI1_SNOOP_1_BASE_HI 0x1f94 +#define GT64260_PCI1_SNOOP_1_SIZE 0x1f98 +#define GT64260_PCI1_SNOOP_2_BASE_LO 0x1fa0 +#define GT64260_PCI1_SNOOP_2_BASE_HI 0x1fa4 +#define GT64260_PCI1_SNOOP_2_SIZE 0x1fa8 +#define GT64260_PCI1_SNOOP_3_BASE_LO 0x1fb0 +#define GT64260_PCI1_SNOOP_3_BASE_HI 0x1fb4 +#define GT64260_PCI1_SNOOP_3_SIZE 0x1fb8 + +/* PCI Error Report Registers */ +#define MV64x60_PCI0_ERR_SERR_MASK 0x0c28 +#define MV64x60_PCI0_ERR_ADDR_LO 0x1d40 +#define MV64x60_PCI0_ERR_ADDR_HI 0x1d44 +#define MV64x60_PCI0_ERR_DATA_LO 0x1d48 +#define MV64x60_PCI0_ERR_DATA_HI 0x1d4c +#define MV64x60_PCI0_ERR_CMD 0x1d50 +#define MV64x60_PCI0_ERR_CAUSE 0x1d58 +#define MV64x60_PCI0_ERR_MASK 0x1d5c + +#define MV64x60_PCI1_ERR_SERR_MASK 0x0ca8 +#define MV64x60_PCI1_ERR_ADDR_LO 0x1dc0 +#define MV64x60_PCI1_ERR_ADDR_HI 0x1dc4 +#define MV64x60_PCI1_ERR_DATA_LO 0x1dc8 +#define MV64x60_PCI1_ERR_DATA_HI 0x1dcc +#define MV64x60_PCI1_ERR_CMD 0x1dd0 +#define MV64x60_PCI1_ERR_CAUSE 0x1dd8 +#define MV64x60_PCI1_ERR_MASK 0x1ddc + +/* PCI Slave Address Decoding Registers */ +#define MV64x60_PCI0_MEM_0_SIZE 0x0c08 +#define MV64x60_PCI0_MEM_1_SIZE 0x0d08 +#define MV64x60_PCI0_MEM_2_SIZE 0x0c0c +#define MV64x60_PCI0_MEM_3_SIZE 0x0d0c +#define MV64x60_PCI1_MEM_0_SIZE 0x0c88 +#define MV64x60_PCI1_MEM_1_SIZE 0x0d88 +#define MV64x60_PCI1_MEM_2_SIZE 0x0c8c +#define MV64x60_PCI1_MEM_3_SIZE 0x0d8c + +#define MV64x60_PCI0_BAR_ENABLE 0x0c3c +#define MV64x60_PCI1_BAR_ENABLE 0x0cbc + +#define MV64x60_PCI0_PCI_DECODE_CNTL 0x0d3c + + + + + +#define MV64x60_PCI0_SLAVE_BAR_REG_ENABLES 0x0c3c +#define MV64x60_PCI0_SLAVE_MEM_0_REMAP 0x0c48 +#define MV64x60_PCI0_SLAVE_MEM_1_REMAP 0x0d48 +#define MV64x60_PCI0_SLAVE_MEM_2_REMAP 0x0c4c +#define MV64x60_PCI0_SLAVE_MEM_3_REMAP 0x0d4c +#define MV64x60_PCI0_SLAVE_CS_0_REMAP 0x0c50 +#define MV64x60_PCI0_SLAVE_CS_1_REMAP 0x0d50 +#define MV64x60_PCI0_SLAVE_CS_2_REMAP 0x0d58 +#define MV64x60_PCI0_SLAVE_CS_3_REMAP 0x0c54 +#define MV64x60_PCI0_SLAVE_BOOT_REMAP 0x0d54 +#define MV64x60_PCI0_SLAVE_P2P_MEM_0_REMAP_LO 0x0d5c +#define MV64x60_PCI0_SLAVE_P2P_MEM_0_REMAP_HI 0x0d60 +#define MV64x60_PCI0_SLAVE_P2P_MEM_1_REMAP_LO 0x0d64 +#define MV64x60_PCI0_SLAVE_P2P_MEM_1_REMAP_HI 0x0d68 +#define MV64x60_PCI0_SLAVE_P2P_IO_REMAP 0x0d6c +#define MV64x60_PCI0_SLAVE_CPU_REMAP 0x0d70 + +#define GT64260_PCI0_SLAVE_DAC_SCS_0_REMAP 0x0f00 +#define GT64260_PCI0_SLAVE_DAC_SCS_1_REMAP 0x0f04 +#define GT64260_PCI0_SLAVE_DAC_SCS_2_REMAP 0x0f08 +#define GT64260_PCI0_SLAVE_DAC_SCS_3_REMAP 0x0f0c +#define GT64260_PCI0_SLAVE_DAC_CS_0_REMAP 0x0f10 +#define GT64260_PCI0_SLAVE_DAC_CS_1_REMAP 0x0f14 +#define GT64260_PCI0_SLAVE_DAC_CS_2_REMAP 0x0f18 +#define GT64260_PCI0_SLAVE_DAC_CS_3_REMAP 0x0f1c +#define GT64260_PCI0_SLAVE_DAC_BOOT_REMAP 0x0f20 +#define GT64260_PCI0_SLAVE_DAC_P2P_MEM_0_REMAP_LO 0x0f24 +#define GT64260_PCI0_SLAVE_DAC_P2P_MEM_0_REMAP_HI 0x0f28 +#define GT64260_PCI0_SLAVE_DAC_P2P_MEM_1_REMAP_LO 0x0f2c +#define GT64260_PCI0_SLAVE_DAC_P2P_MEM_1_REMAP_HI 0x0f30 +#define GT64260_PCI0_SLAVE_DAC_CPU_REMAP 0x0f34 + +#define GT64260_PCI0_SLAVE_EXP_ROM_REMAP 0x0f38 +#define GT64260_PCI0_SLAVE_PCI_DECODE_CNTL 0x0d3c + + + + + +/* XXXX BEGIN */ +#define MV64x60_PCI1_PCI_DECODE_CNTL 0x0dbc + +#define MV64x60_PCI1_SLAVE_MEM_0_SIZE 0x0c88 +#define MV64x60_PCI1_SLAVE_MEM_1_SIZE 0x0d88 +#define MV64x60_PCI1_SLAVE_MEM_2_SIZE 0x0c8c +#define MV64x60_PCI1_SLAVE_MEM_3_SIZE 0x0d8c +#define MV64x60_PCI1_SLAVE_CS_0_SIZE 0x0c90 +#define MV64x60_PCI1_SLAVE_CS_1_SIZE 0x0d90 +#define MV64x60_PCI1_SLAVE_CS_2_SIZE 0x0d98 +#define MV64x60_PCI1_SLAVE_CS_3_SIZE 0x0c94 +#define MV64x60_PCI1_SLAVE_BOOT_SIZE 0x0d94 +#define MV64x60_PCI1_SLAVE_P2P_MEM_0_SIZE 0x0d9c +#define MV64x60_PCI1_SLAVE_P2P_MEM_1_SIZE 0x0da0 +#define MV64x60_PCI1_SLAVE_P2P_IO_SIZE 0x0da4 +#define MV64x60_PCI1_SLAVE_CPU_SIZE 0x0da8 + + +/* XXXXX END */ + + +#define GT64260_PCI1_SLAVE_DAC_SCS_0_SIZE 0x0e80 +#define GT64260_PCI1_SLAVE_DAC_SCS_1_SIZE 0x0e84 +#define GT64260_PCI1_SLAVE_DAC_SCS_2_SIZE 0x0e88 +#define GT64260_PCI1_SLAVE_DAC_SCS_3_SIZE 0x0e8c +#define GT64260_PCI1_SLAVE_DAC_CS_0_SIZE 0x0e90 +#define GT64260_PCI1_SLAVE_DAC_CS_1_SIZE 0x0e94 +#define GT64260_PCI1_SLAVE_DAC_CS_2_SIZE 0x0e98 +#define GT64260_PCI1_SLAVE_DAC_CS_3_SIZE 0x0e9c +#define GT64260_PCI1_SLAVE_DAC_BOOT_SIZE 0x0ea0 +#define GT64260_PCI1_SLAVE_DAC_P2P_MEM_0_SIZE 0x0ea4 +#define GT64260_PCI1_SLAVE_DAC_P2P_MEM_1_SIZE 0x0ea8 +#define GT64260_PCI1_SLAVE_DAC_CPU_SIZE 0x0eac + +#define GT64260_PCI1_SLAVE_EXP_ROM_SIZE 0x0dac + + + + +/* XXXX BEGIN */ + +#define MV64x60_PCI1_SLAVE_BAR_REG_ENABLES 0x0cbc +#define MV64x60_PCI1_SLAVE_MEM_0_REMAP 0x0cc8 +#define MV64x60_PCI1_SLAVE_MEM_1_REMAP 0x0dc8 +#define MV64x60_PCI1_SLAVE_MEM_2_REMAP 0x0ccc +#define MV64x60_PCI1_SLAVE_MEM_3_REMAP 0x0dcc +#define MV64x60_PCI1_SLAVE_CS_0_REMAP 0x0cd0 +#define MV64x60_PCI1_SLAVE_CS_1_REMAP 0x0dd0 +#define MV64x60_PCI1_SLAVE_CS_2_REMAP 0x0dd8 +#define MV64x60_PCI1_SLAVE_CS_3_REMAP 0x0cd4 +#define MV64x60_PCI1_SLAVE_BOOT_REMAP 0x0dd4 +#define MV64x60_PCI1_SLAVE_P2P_MEM_0_REMAP_LO 0x0ddc +#define MV64x60_PCI1_SLAVE_P2P_MEM_0_REMAP_HI 0x0de0 +#define MV64x60_PCI1_SLAVE_P2P_MEM_1_REMAP_LO 0x0de4 +#define MV64x60_PCI1_SLAVE_P2P_MEM_1_REMAP_HI 0x0de8 +#define MV64x60_PCI1_SLAVE_P2P_IO_REMAP 0x0dec +#define MV64x60_PCI1_SLAVE_CPU_REMAP 0x0df0 + +/* XXXXX END */ + + + +#define GT64260_PCI1_SLAVE_DAC_SCS_0_REMAP 0x0f80 +#define GT64260_PCI1_SLAVE_DAC_SCS_1_REMAP 0x0f84 +#define GT64260_PCI1_SLAVE_DAC_SCS_2_REMAP 0x0f88 +#define GT64260_PCI1_SLAVE_DAC_SCS_3_REMAP 0x0f8c +#define GT64260_PCI1_SLAVE_DAC_CS_0_REMAP 0x0f90 +#define GT64260_PCI1_SLAVE_DAC_CS_1_REMAP 0x0f94 +#define GT64260_PCI1_SLAVE_DAC_CS_2_REMAP 0x0f98 +#define GT64260_PCI1_SLAVE_DAC_CS_3_REMAP 0x0f9c +#define GT64260_PCI1_SLAVE_DAC_BOOT_REMAP 0x0fa0 +#define GT64260_PCI1_SLAVE_DAC_P2P_MEM_0_REMAP_LO 0x0fa4 +#define GT64260_PCI1_SLAVE_DAC_P2P_MEM_0_REMAP_HI 0x0fa8 +#define GT64260_PCI1_SLAVE_DAC_P2P_MEM_1_REMAP_LO 0x0fac +#define GT64260_PCI1_SLAVE_DAC_P2P_MEM_1_REMAP_HI 0x0fb0 +#define GT64260_PCI1_SLAVE_DAC_CPU_REMAP 0x0fb4 + +#define GT64260_PCI1_SLAVE_EXP_ROM_REMAP 0x0fb8 +#define GT64260_PCI1_SLAVE_PCI_DECODE_CNTL 0x0dbc + + +/* + ***************************************************************************** + * + * Timer/Counter Interface Registers + * + ***************************************************************************** + */ + +#define MV64x60_TIMR_CNTR_0 0x0850 +#define MV64x60_TIMR_CNTR_1 0x0854 +#define MV64x60_TIMR_CNTR_2 0x0858 +#define MV64x60_TIMR_CNTR_3 0x085c +#define MV64x60_TIMR_CNTR_0_3_CNTL 0x0864 +#define MV64x60_TIMR_CNTR_0_3_INTR_CAUSE 0x0868 +#define MV64x60_TIMR_CNTR_0_3_INTR_MASK 0x086c + +#define GT64260_TIMR_CNTR_4 0x0950 +#define GT64260_TIMR_CNTR_5 0x0954 +#define GT64260_TIMR_CNTR_6 0x0958 +#define GT64260_TIMR_CNTR_7 0x095c +#define GT64260_TIMR_CNTR_4_7_CNTL 0x0964 +#define GT64260_TIMR_CNTR_4_7_INTR_CAUSE 0x0968 +#define GT64260_TIMR_CNTR_4_7_INTR_MASK 0x096c + + +/* + ***************************************************************************** + * + * Communications Controller (Enet, Serial, etc.) Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_COMM_ENET_0_OFFSET 0xf200 +#define GT64260_COMM_ENET_1_OFFSET 0xf220 +#define GT64260_COMM_ENET_2_OFFSET 0xf240 + +#define GT64260_ENET_CNTL_LO \ + (0xf200 - GT64260_COMM_ENET_0_BASE) +#define GT64260_ENET_CNTL_HI \ + (0xf204 - GT64260_COMM_ENET_0_BASE) +#define GT64260_ENET_RX_BUF_PCI_ADDR_HI \ + (0xf208 - GT64260_COMM_ENET_0_BASE) +#define GT64260_ENET_TX_BUF_PCI_ADDR_HI \ + (0xf20c - GT64260_COMM_ENET_0_BASE) +#define GT64260_ENET_RX_DESC_ADDR_HI \ + (0xf210 - GT64260_COMM_ENET_0_BASE) +#define GT64260_ENET_TX_DESC_ADDR_HI \ + (0xf214 - GT64260_COMM_ENET_0_BASE) +#define GT64260_ENET_HASH_TAB_PCI_ADDR_HI \ + (0xf218 - GT64260_COMM_ENET_0_BASE) + +#define GT64260_COMM_MPSC_0_OFFSET 0xf280 +#define GT64260_COMM_MPSC_1_OFFSET 0xf2c0 + +#define GT64260_MPSC_CNTL_LO \ + (0xf280 - GT64260_COMM_MPSC_0_BASE) +#define GT64260_MPSC_CNTL_HI \ + (0xf284 - GT64260_COMM_MPSC_0_BASE) +#define GT64260_MPSC_RX_BUF_PCI_ADDR_HI \ + (0xf288 - GT64260_COMM_MPSC_0_BASE) +#define GT64260_MPSC_TX_BUF_PCI_ADDR_HI \ + (0xf28c - GT64260_COMM_MPSC_0_BASE) +#define GT64260_MPSC_RX_DESC_ADDR_HI \ + (0xf290 - GT64260_COMM_MPSC_0_BASE) +#define GT64260_MPSC_TX_DESC_ADDR_HI \ + (0xf294 - GT64260_COMM_MPSC_0_BASE) + +#define GT64260_SER_INIT_PCI_ADDR_HI 0xf320 +#define GT64260_SER_INIT_LAST_DATA 0xf324 +#define GT64260_SER_INIT_CONTROL 0xf328 +#define GT64260_SER_INIT_STATUS 0xf32c + +#define GT64260_COMM_ARBITER_CNTL 0xf300 +#define GT64260_COMM_CONFIG 0xb40c +#define GT64260_COMM_XBAR_TO 0xf304 +#define GT64260_COMM_INTR_CAUSE 0xf310 +#define GT64260_COMM_INTR_MASK 0xf314 +#define GT64260_COMM_ERR_ADDR 0xf318 + + +/* + ***************************************************************************** + * + * Fast Ethernet Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_ENET_PHY_ADDR 0x2000 +#define GT64260_ENET_ESMIR 0x2010 + +#define GT64260_ENET_0_OFFSET 0x2400 +#define GT64260_ENET_1_OFFSET 0x2800 +#define GT64260_ENET_2_OFFSET 0x2c00 + +#define GT64260_ENET_EPCR (0x2400 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EPCXR (0x2408 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EPCMR (0x2410 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EPSR (0x2418 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ESPR (0x2420 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EHTPR (0x2428 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EFCSAL (0x2430 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EFCSAH (0x2438 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ESDCR (0x2440 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ESDCMR (0x2448 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EICR (0x2450 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EIMR (0x2458 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EFRDP0 (0x2480 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EFRDP1 (0x2484 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EFRDP2 (0x2488 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_EFRDP3 (0x248c - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ECRDP0 (0x24a0 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ECRDP1 (0x24a4 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ECRDP2 (0x24a8 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ECRDP3 (0x24ac - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ECTDP0 (0x24e0 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_ECTDP1 (0x24e4 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_DSCP2P0L (0x2460 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_DSCP2P0H (0x2464 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_DSCP2P1L (0x2468 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_DSCP2P1H (0x246c - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_VPT2P (0x2470 - GT64260_ENET_0_OFFSET) +#define GT64260_ENET_MIB_CTRS (0x2500 - GT64260_ENET_0_OFFSET) + +/* + ***************************************************************************** + * + * IDMA Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_IDMA_0_OFFSET 0x0800 +#define GT64260_IDMA_1_OFFSET 0x0804 +#define GT64260_IDMA_2_OFFSET 0x0808 +#define GT64260_IDMA_3_OFFSET 0x080c +#define GT64260_IDMA_4_OFFSET 0x0900 +#define GT64260_IDMA_5_OFFSET 0x0904 +#define GT64260_IDMA_6_OFFSET 0x0908 +#define GT64260_IDMA_7_OFFSET 0x090c + +#define GT64260_IDMA_BYTE_COUNT (0x0800 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_SRC_ADDR (0x0810 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_DST_ADDR (0x0820 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_NEXT_DESC (0x0830 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_CUR_DESC (0x0870 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_SRC_PCI_ADDR_HI (0x0890 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_DST_PCI_ADDR_HI (0x08a0 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_NEXT_DESC_PCI_ADDR_HI (0x08b0 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_CONTROL_LO (0x0840 - GT64260_IDMA_0_OFFSET) +#define GT64260_IDMA_CONTROL_HI (0x0880 - GT64260_IDMA_0_OFFSET) + +#define GT64260_IDMA_0_3_ARBITER_CNTL 0x0860 +#define GT64260_IDMA_4_7_ARBITER_CNTL 0x0960 + +#define GT64260_IDMA_0_3_XBAR_TO 0x08d0 +#define GT64260_IDMA_4_7_XBAR_TO 0x09d0 + +#define GT64260_IDMA_0_3_INTR_CAUSE 0x08c0 +#define GT64260_IDMA_0_3_INTR_MASK 0x08c4 +#define GT64260_IDMA_0_3_ERROR_ADDR 0x08c8 +#define GT64260_IDMA_0_3_ERROR_SELECT 0x08cc +#define GT64260_IDMA_4_7_INTR_CAUSE 0x09c0 +#define GT64260_IDMA_4_7_INTR_MASK 0x09c4 +#define GT64260_IDMA_4_7_ERROR_ADDR 0x09c8 +#define GT64260_IDMA_4_7_ERROR_SELECT 0x09cc + +/* + ***************************************************************************** + * + * Watchdog Timer Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_WDT_WDC 0xb410 +#define GT64260_WDT_WDV 0xb414 + + +/* + ***************************************************************************** + * + * General Purpose Pins Controller Interface Registers + * + ***************************************************************************** + */ + +#define MV64x60_GPP_IO_CNTL 0xf100 +#define MV64x60_GPP_LEVEL_CNTL 0xf110 +#define MV64x60_GPP_VALUE 0xf104 +#define MV64x60_GPP_INTR_CAUSE 0xf108 +#define MV64x60_GPP_INTR_MASK 0xf10c + + +/* + ***************************************************************************** + * + * Multi-Purpose Pins Controller Interface Registers + * + ***************************************************************************** + */ + +#define MV64x60_MPP_CNTL_0 0xf000 +#define MV64x60_MPP_CNTL_1 0xf004 +#define MV64x60_MPP_CNTL_2 0xf008 +#define MV64x60_MPP_CNTL_3 0xf00c +#define GT64260_MPP_SERIAL_PORTS_MULTIPLEX 0xf010 + + +/* + ***************************************************************************** + * + * I2C Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_I2C_OFFSET 0xc000 + +#define GT64260_I2C_ADDR (0xc000 - GT64260_I2C_OFFSET) +#define GT64260_I2C_EX_ADDR (0xc010 - GT64260_I2C_OFFSET) +#define GT64260_I2C_DATA (0xc004 - GT64260_I2C_OFFSET) +#define GT64260_I2C_CONTROL (0xc008 - GT64260_I2C_OFFSET) +#define GT64260_I2C_STATUS (0xc00c - GT64260_I2C_OFFSET) +#define GT64260_I2C_BAUD_RATE (0xc00c - GT64260_I2C_OFFSET) +#define GT64260_I2C_RESET (0xc01c - GT64260_I2C_OFFSET) + +#define GT64260_I2C_ACK_BIT (1<<2) +#define GT64260_I2C_IFLG_BIT (1<<3) +#define GT64260_I2C_STOP_BIT (1<<4) +#define GT64260_I2C_START_BIT (1<<5) +#define GT64260_I2C_ENABLE_BIT (1<<6) +#define GT64260_I2C_INT_ENABLE_BIT (1<<7) + +#define GT64260_I2C_DATA_READ_BIT 0x01 + +#define GT64260_I2C_STATUS_SENT_START 0x08 +#define GT64260_I2C_STATUS_RESENT_START 0x10 +#define GT64260_I2C_STATUS_WRITE_ADDR_ACK 0x18 +#define GT64260_I2C_STATUS_WRITE_ACK 0x28 +#define GT64260_I2C_STATUS_READ_ADDR_ACK 0x40 +#define GT64260_I2C_STATUS_READ_ACK 0x50 +#define GT64260_I2C_STATUS_READ_NO_ACK 0x58 +#define GT64260_I2C_STATUS_IDLE 0xf8 + + +/* + ***************************************************************************** + * + * Interrupt Controller Interface Registers + * + ***************************************************************************** + */ + +#define GT64260_IC_OFFSET 0x0c18 + +#define GT64260_IC_MAIN_CAUSE_LO (0x0c18 - GT64260_IC_OFFSET) +#define GT64260_IC_MAIN_CAUSE_HI (0x0c68 - GT64260_IC_OFFSET) +#define GT64260_IC_CPU_INTR_MASK_LO (0x0c1c - GT64260_IC_OFFSET) +#define GT64260_IC_CPU_INTR_MASK_HI (0x0c6c - GT64260_IC_OFFSET) +#define GT64260_IC_CPU_SELECT_CAUSE (0x0c70 - GT64260_IC_OFFSET) +#define GT64260_IC_PCI0_INTR_MASK_LO (0x0c24 - GT64260_IC_OFFSET) +#define GT64260_IC_PCI0_INTR_MASK_HI (0x0c64 - GT64260_IC_OFFSET) +#define GT64260_IC_PCI0_SELECT_CAUSE (0x0c74 - GT64260_IC_OFFSET) +#define GT64260_IC_PCI1_INTR_MASK_LO (0x0ca4 - GT64260_IC_OFFSET) +#define GT64260_IC_PCI1_INTR_MASK_HI (0x0ce4 - GT64260_IC_OFFSET) +#define GT64260_IC_PCI1_SELECT_CAUSE (0x0cf4 - GT64260_IC_OFFSET) +#define GT64260_IC_CPU_INT_0_MASK (0x0e60 - GT64260_IC_OFFSET) +#define GT64260_IC_CPU_INT_1_MASK (0x0e64 - GT64260_IC_OFFSET) +#define GT64260_IC_CPU_INT_2_MASK (0x0e68 - GT64260_IC_OFFSET) +#define GT64260_IC_CPU_INT_3_MASK (0x0e6c - GT64260_IC_OFFSET) + +#define MV64360_IC_OFFSET 0x0000 + +#define MV64360_IC_MAIN_CAUSE_LO (0x0004 - MV64360_IC_OFFSET) +#define MV64360_IC_MAIN_CAUSE_HI (0x000c - MV64360_IC_OFFSET) +#define MV64360_IC_CPU0_INTR_MASK_LO (0x0014 - MV64360_IC_OFFSET) +#define MV64360_IC_CPU0_INTR_MASK_HI (0x001c - MV64360_IC_OFFSET) +#define MV64360_IC_CPU0_SELECT_CAUSE (0x0024 - MV64360_IC_OFFSET) +#define MV64360_IC_CPU1_INTR_MASK_LO (0x0034 - MV64360_IC_OFFSET) +#define MV64360_IC_CPU1_INTR_MASK_HI (0x003c - MV64360_IC_OFFSET) +#define MV64360_IC_CPU1_SELECT_CAUSE (0x0044 - MV64360_IC_OFFSET) +#define MV64360_IC_INT0_MASK_LO (0x0054 - MV64360_IC_OFFSET) +#define MV64360_IC_INT0_MASK_HI (0x005c - MV64360_IC_OFFSET) +#define MV64360_IC_INT0_SELECT_CAUSE (0x0064 - MV64360_IC_OFFSET) +#define MV64360_IC_INT1_MASK_LO (0x0074 - MV64360_IC_OFFSET) +#define MV64360_IC_INT1_MASK_HI (0x007c - MV64360_IC_OFFSET) +#define MV64360_IC_INT1_SELECT_CAUSE (0x0084 - MV64360_IC_OFFSET) + +#endif /* __ASMPPC_MV64x60_DEFS_H */ diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h new file mode 100644 index 000000000..d522d43d4 --- /dev/null +++ b/include/linux/mtd/physmap.h @@ -0,0 +1,61 @@ +/* + * For boards with physically mapped flash and using + * drivers/mtd/maps/physmap.c mapping driver. + * + * $Id: physmap.h,v 1.2 2004/07/14 17:48:46 dwmw2 Exp $ + * + * Copyright (C) 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __LINUX_MTD_PHYSMAP__ + +#include + +#if defined(CONFIG_MTD_PHYSMAP) + +#include +#include +#include + +/* + * The map_info for physmap. Board can override size, buswidth, phys, + * (*set_vpp)(), etc in their initial setup routine. + */ +extern struct map_info physmap_map; + +/* + * Board needs to specify the exact mapping during their setup time. + */ +static inline void physmap_configure(unsigned long addr, unsigned long size, int buswidth, void (*set_vpp)(struct map_info *, int) ) +{ + physmap_map.phys = addr; + physmap_map.size = size; + physmap_map.buswidth = buswidth; + physmap_map.set_vpp = set_vpp; +} + +#if defined(CONFIG_MTD_PARTITIONS) + +/* + * Machines that wish to do flash partition may want to call this function in + * their setup routine. + * + * physmap_set_partitions(mypartitions, num_parts); + * + * Note that one can always override this hard-coded partition with + * command line partition (you need to enable CONFIG_MTD_CMDLINE_PARTS). + */ +void physmap_set_partitions(struct mtd_partition *parts, int num_parts); + +#endif /* defined(CONFIG_MTD_PARTITIONS) */ +#endif /* defined(CONFIG_MTD) */ + +#endif /* __LINUX_MTD_PHYSMAP__ */ + diff --git a/include/mtd/inftl-user.h b/include/mtd/inftl-user.h new file mode 100644 index 000000000..bda4f2c8f --- /dev/null +++ b/include/mtd/inftl-user.h @@ -0,0 +1,91 @@ +/* + * $Id: inftl-user.h,v 1.1 2004/05/05 15:17:00 dwmw2 Exp $ + * + * Parts of INFTL headers shared with userspace + * + */ + +#ifndef __MTD_INFTL_USER_H__ +#define __MTD_INFTL_USER_H__ + +#define OSAK_VERSION 0x5120 +#define PERCENTUSED 98 + +#define SECTORSIZE 512 + +/* Block Control Information */ + +struct inftl_bci { + uint8_t ECCsig[6]; + uint8_t Status; + uint8_t Status1; +} __attribute__((packed)); + +struct inftl_unithead1 { + uint16_t virtualUnitNo; + uint16_t prevUnitNo; + uint8_t ANAC; + uint8_t NACs; + uint8_t parityPerField; + uint8_t discarded; +} __attribute__((packed)); + +struct inftl_unithead2 { + uint8_t parityPerField; + uint8_t ANAC; + uint16_t prevUnitNo; + uint16_t virtualUnitNo; + uint8_t NACs; + uint8_t discarded; +} __attribute__((packed)); + +struct inftl_unittail { + uint8_t Reserved[4]; + uint16_t EraseMark; + uint16_t EraseMark1; +} __attribute__((packed)); + +union inftl_uci { + struct inftl_unithead1 a; + struct inftl_unithead2 b; + struct inftl_unittail c; +}; + +struct inftl_oob { + struct inftl_bci b; + union inftl_uci u; +}; + + +/* INFTL Media Header */ + +struct INFTLPartition { + __u32 virtualUnits; + __u32 firstUnit; + __u32 lastUnit; + __u32 flags; + __u32 spareUnits; + __u32 Reserved0; + __u32 Reserved1; +} __attribute__((packed)); + +struct INFTLMediaHeader { + char bootRecordID[8]; + __u32 NoOfBootImageBlocks; + __u32 NoOfBinaryPartitions; + __u32 NoOfBDTLPartitions; + __u32 BlockMultiplierBits; + __u32 FormatFlags; + __u32 OsakVersion; + __u32 PercentUsed; + struct INFTLPartition Partitions[4]; +} __attribute__((packed)); + +/* Partition flag types */ +#define INFTL_BINARY 0x20000000 +#define INFTL_BDTL 0x40000000 +#define INFTL_LAST 0x80000000 + +#endif /* __MTD_INFTL_USER_H__ */ + + diff --git a/include/mtd/jffs2-user.h b/include/mtd/jffs2-user.h new file mode 100644 index 000000000..d508ef0ae --- /dev/null +++ b/include/mtd/jffs2-user.h @@ -0,0 +1,35 @@ +/* + * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $ + * + * JFFS2 definitions for use in user space only + */ + +#ifndef __JFFS2_USER_H__ +#define __JFFS2_USER_H__ + +/* This file is blessed for inclusion by userspace */ +#include +#include +#include + +#undef cpu_to_je16 +#undef cpu_to_je32 +#undef cpu_to_jemode +#undef je16_to_cpu +#undef je32_to_cpu +#undef jemode_to_cpu + +extern int target_endian; + +#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); }) +#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); }) + +#define cpu_to_je16(x) ((jint16_t){t16(x)}) +#define cpu_to_je32(x) ((jint32_t){t32(x)}) +#define cpu_to_jemode(x) ((jmode_t){t32(x)}) + +#define je16_to_cpu(x) (t16((x).v16)) +#define je32_to_cpu(x) (t32((x).v32)) +#define jemode_to_cpu(x) (t32((x).m)) + +#endif /* __JFFS2_USER_H__ */ diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h new file mode 100644 index 000000000..db5fca2fa --- /dev/null +++ b/include/mtd/mtd-abi.h @@ -0,0 +1,97 @@ +/* + * $Id: mtd-abi.h,v 1.5 2004/06/22 09:29:35 gleixner Exp $ + * + * Portions of MTD ABI definition which are shared by kernel and user space + */ + +#ifndef __MTD_ABI_H__ +#define __MTD_ABI_H__ + +struct erase_info_user { + uint32_t start; + uint32_t length; +}; + +struct mtd_oob_buf { + uint32_t start; + uint32_t length; + unsigned char *ptr; +}; + +#define MTD_ABSENT 0 +#define MTD_RAM 1 +#define MTD_ROM 2 +#define MTD_NORFLASH 3 +#define MTD_NANDFLASH 4 +#define MTD_PEROM 5 +#define MTD_OTHER 14 +#define MTD_UNKNOWN 15 + +#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash) +#define MTD_SET_BITS 2 // Bits can be set +#define MTD_ERASEABLE 4 // Has an erase function +#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible +#define MTD_VOLATILE 16 // Set for RAMs +#define MTD_XIP 32 // eXecute-In-Place possible +#define MTD_OOB 64 // Out-of-band data (NAND flash) +#define MTD_ECC 128 // Device capable of automatic ECC + +// Some common devices / combinations of capabilities +#define MTD_CAP_ROM 0 +#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE) +#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE) +#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB) +#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS) + + +// Types of automatic ECC/Checksum available +#define MTD_ECC_NONE 0 // No automatic ECC available +#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip +#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices + +/* ECC byte placement */ +#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) +#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) +#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme +#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) + +struct mtd_info_user { + uint8_t type; + uint32_t flags; + uint32_t size; // Total size of the MTD + uint32_t erasesize; + uint32_t oobblock; // Size of OOB blocks (e.g. 512) + uint32_t oobsize; // Amount of OOB data per block (e.g. 16) + uint32_t ecctype; + uint32_t eccsize; +}; + +struct region_info_user { + uint32_t offset; /* At which this region starts, + * from the beginning of the MTD */ + uint32_t erasesize; /* For this region */ + uint32_t numblocks; /* Number of blocks in this region */ + uint32_t regionindex; +}; + +#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) +#define MEMERASE _IOW('M', 2, struct erase_info_user) +#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) +#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) +#define MEMLOCK _IOW('M', 5, struct erase_info_user) +#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) +#define MEMGETREGIONCOUNT _IOR('M', 7, int) +#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) +#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) +#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) +#define MEMGETBADBLOCK _IOW('M', 11, loff_t) +#define MEMSETBADBLOCK _IOW('M', 12, loff_t) + +struct nand_oobinfo { + uint32_t useecc; + uint32_t eccbytes; + uint32_t oobfree[8][2]; + uint32_t eccpos[32]; +}; + +#endif /* __MTD_ABI_H__ */ diff --git a/include/mtd/nftl-user.h b/include/mtd/nftl-user.h new file mode 100644 index 000000000..924ec0459 --- /dev/null +++ b/include/mtd/nftl-user.h @@ -0,0 +1,76 @@ +/* + * $Id: nftl-user.h,v 1.1 2004/05/05 14:44:57 dwmw2 Exp $ + * + * Parts of NFTL headers shared with userspace + * + */ + +#ifndef __MTD_NFTL_USER_H__ +#define __MTD_NFTL_USER_H__ + +/* Block Control Information */ + +struct nftl_bci { + unsigned char ECCSig[6]; + uint8_t Status; + uint8_t Status1; +}__attribute__((packed)); + +/* Unit Control Information */ + +struct nftl_uci0 { + uint16_t VirtUnitNum; + uint16_t ReplUnitNum; + uint16_t SpareVirtUnitNum; + uint16_t SpareReplUnitNum; +} __attribute__((packed)); + +struct nftl_uci1 { + uint32_t WearInfo; + uint16_t EraseMark; + uint16_t EraseMark1; +} __attribute__((packed)); + +struct nftl_uci2 { + uint16_t FoldMark; + uint16_t FoldMark1; + uint32_t unused; +} __attribute__((packed)); + +union nftl_uci { + struct nftl_uci0 a; + struct nftl_uci1 b; + struct nftl_uci2 c; +}; + +struct nftl_oob { + struct nftl_bci b; + union nftl_uci u; +}; + +/* NFTL Media Header */ + +struct NFTLMediaHeader { + char DataOrgID[6]; + uint16_t NumEraseUnits; + uint16_t FirstPhysicalEUN; + uint32_t FormattedSize; + unsigned char UnitSizeFactor; +} __attribute__((packed)); + +#define MAX_ERASE_ZONES (8192 - 512) + +#define ERASE_MARK 0x3c69 +#define SECTOR_FREE 0xff +#define SECTOR_USED 0x55 +#define SECTOR_IGNORE 0x11 +#define SECTOR_DELETED 0x00 + +#define FOLD_MARK_IN_PROGRESS 0x5555 + +#define ZONE_GOOD 0xff +#define ZONE_BAD_ORIGINAL 0 +#define ZONE_BAD_MARKED 7 + + +#endif /* __MTD_NFTL_USER_H__ */