--- nand.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ nand.c 2007-10-16 12:31:59.000000000 +0200 @@ -0,0 +1,579 @@ +/* + * Au1xxx NAND platform device registration + * + * Copyright (C) 2004 Embedded Edge, LLC + * Copyright (C) 2007 Florian Fainelli + * + * 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 + + +static int nand_width = 1; /* default x8 */ +static void (*au1550_write_byte)(struct mtd_info *, u_char); + +/* + * Define partitions for flash device + */ +static const struct mtd_partition partition_info[] = { + [0] = { + .name = "NAND FS 0", + .offset = 0, + .size = 8 * 1024 * 1024 + }, + [1] = { + .name = "NAND FS 1", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; + +/** + * au_read_byte - read one byte from the chip + * @mtd: MTD device structure + * + * read function for 8bit buswith + */ +static u_char au_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + u_char ret = readb(this->IO_ADDR_R); + au_sync(); + return ret; +} + +/** + * au_write_byte - write one byte to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * write function for 8it buswith + */ +static void au_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nand_chip *this = mtd->priv; + writeb(byte, this->IO_ADDR_W); + au_sync(); +} + +/** + * au_read_byte16 - read one byte endianess aware from the chip + * @mtd: MTD device structure + * + * read function for 16bit buswith with + * endianess conversion + */ +static u_char au_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R)); + au_sync(); + return ret; +} + +/** + * au_write_byte16 - write one byte endianess aware to the chip + * @mtd: MTD device structure + * @byte: pointer to data byte to write + * + * write function for 16bit buswith with + * endianess conversion + */ +static void au_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); + au_sync(); +} + +/** + * au_read_word - read one word from the chip + * @mtd: MTD device structure + * + * read function for 16bit buswith without + * endianess conversion + */ +static u16 au_read_word(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + u16 ret = readw(this->IO_ADDR_R); + au_sync(); + return ret; +} + +/** + * au_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * write function for 8bit buswith + */ +static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) { + writeb(buf[i], this->IO_ADDR_W); + au_sync(); + } +} + +/** + * au_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * read function for 8bit buswith + */ +static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) { + buf[i] = readb(this->IO_ADDR_R); + au_sync(); + } +} + +/** + * au_verify_buf - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * verify function for 8bit buswith + */ +static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) { + if (buf[i] != readb(this->IO_ADDR_R)) + return -EFAULT; + au_sync(); + } + + return 0; +} + +/** + * au_write_buf16 - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * write function for 16bit buswith + */ +static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) { + writew(p[i], this->IO_ADDR_W); + au_sync(); + } + +} + +/** + * au_read_buf16 - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * read function for 16bit buswith + */ +static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) { + p[i] = readw(this->IO_ADDR_R); + au_sync(); + } +} + +/** + * au_verify_buf16 - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * verify function for 16bit buswith + */ +static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) { + if (p[i] != readw(this->IO_ADDR_R)) + return -EFAULT; + au_sync(); + } + return 0; +} + +/* Select the chip by setting nCE to low */ +#define NAND_CTL_SETNCE 1 +/* Deselect the chip by setting nCE to high */ +#define NAND_CTL_CLRNCE 2 +/* Select the command latch by setting CLE to high */ +#define NAND_CTL_SETCLE 3 +/* Deselect the command latch by setting CLE to low */ +#define NAND_CTL_CLRCLE 4 +/* Select the address latch by setting ALE to high */ +#define NAND_CTL_SETALE 5 +/* Deselect the address latch by setting ALE to low */ +#define NAND_CTL_CLRALE 6 + +static void au1550_hwcontrol(struct mtd_info *mtd, int dat, unsigned int cmd) +{ + register struct nand_chip *this = mtd->priv; + + switch (cmd) { + case NAND_CTL_SETCLE: + this->IO_ADDR_W = this->io_base + MEM_STNAND_CMD; + break; + case NAND_CTL_CLRCLE: + this->IO_ADDR_W = this->io_base + MEM_STNAND_DATA; + break; + case NAND_CTL_SETALE: + this->IO_ADDR_W = this->io_base + MEM_STNAND_ADDR; + break; + case NAND_CTL_CLRALE: + this->IO_ADDR_W = this->io_base + MEM_STNAND_DATA; + /* FIXME: Nobody knows why this is necessary, + * but it works only that way */ + udelay(1); + break; + case NAND_CTL_SETNCE: + /* assert (force assert) chip enable */ + au_writel((1 << (4 + NAND_CS)), MEM_STNDCTL); + break; + case NAND_CTL_CLRNCE: + /* deassert chip enable */ + au_writel(0, MEM_STNDCTL); + break; + } + + this->IO_ADDR_R = this->IO_ADDR_W; + + /* Drain the writebuffer */ + au_sync(); +} + +int au1550_device_ready(struct mtd_info *mtd) +{ + int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0; + au_sync(); + return ret; +} + +/** + * au1550_select_chip - control -CE line + * Forbid driving -CE manually permitting the NAND controller to do this. + * Keeping -CE asserted during the whole sector reads interferes with the + * NOR flash and PCMCIA drivers as it causes contention on the static bus. + * We only have to hold -CE low for the NAND read commands since the flash + * chip needs it to be asserted during chip not ready time but the NAND + * controller keeps it released. + * + * @mtd: MTD device structure + * @chip: chipnumber to select, -1 for deselect + */ +static void au1550_select_chip(struct mtd_info *mtd, int chip) +{ +} + +/** + * au1550_command - 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 + */ +static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + register struct nand_chip *this = mtd->priv; + int ce_override = 0, i; + ulong flags; + + /* Begin command latch cycle */ + au1550_hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + au1550_write_byte(mtd, readcmd); + } + au1550_write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ + au1550_hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + au1550_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; + au1550_write_byte(mtd, column); + } + if (page_addr != -1) { + au1550_write_byte(mtd, (u8)(page_addr & 0xff)); + + if (command == NAND_CMD_READ0 || + command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB) { + /* + * NAND controller will release -CE after + * the last address byte is written, so we'll + * have to forcibly assert it. No interrupts + * are allowed while we do this as we don't + * want the NOR flash or PCMCIA drivers to + * steal our precious bytes of data... + */ + ce_override = 1; + local_irq_save(flags); + au1550_hwcontrol(mtd, NAND_CTL_SETNCE); + } + + au1550_write_byte(mtd, (u8)(page_addr >> 8)); + + /* One more address cycle for devices > 32MiB */ + if (this->chipsize > (32 << 20)) + au1550_write_byte(mtd, (u8)((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ + au1550_hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* + * Program and erase have their own busy handlers. + * Status and sequential in need 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: + break; + + case NAND_CMD_READ0: + case NAND_CMD_READ1: + case NAND_CMD_READOOB: + /* Check if we're really driving -CE low (just in case) */ + if (unlikely(!ce_override)) + break; + + /* Apply a short delay always to ensure that we do wait tWB. */ + ndelay(100); + /* Wait for a chip to become ready... */ + for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i) + udelay(1); + + /* Release -CE and re-enable interrupts. */ + au1550_hwcontrol(mtd, NAND_CTL_CLRNCE); + local_irq_restore(flags); + return; + } + /* Apply this short delay always to ensure that we do wait tWB. */ + ndelay(100); + + while(!this->dev_ready(mtd)); +} + + +static const struct mtd_partition[] au1xx_nand_parts[] = { + { + .name = "NAND FS 0", + .offset = 0, + .size = 8 * 1024 * 1024, + }, + { + .name = "NAND FS 1", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; + +static struct resource au1xxx_nand_slot0 = { + [0] = { + .name = "nand_membase", + .flags = IORESOURCE_MEM, + } +}; + +struct platform_nand_data au1xxx_nand_data = { + .ctrl.dev_ready = au1550_device_ready, + .ctrl.cmdfunc = au1550_command, + .ctrl.select_chip = au1550_select_chip, + .chip.nr_chip = 1; + .chip.nr_partitions = ARRAY_SIZE(au1xxx_nand_parts), + .chip.partitions = au1xxx_nand_parts, + .chip.chip_delay = 30, + .chip.ecc_mode = NAND_ECC_SOFT, + .chip.options = NAND_NO_AUTOINCR, +}; + +static struct platform_device au1xxx_nand_device = { + .name = "gen_nand", + .id = -1, + .resource = au1xxx_nand_slot0, + .num_resources = ARRAY_SIZE(au1xxx_nand_slot0), + .dev.platform_data = &au1xxx_nand_data, +}; + +/* + * Main initialization routine + */ +static int __init au1xxx_nand_init(void) +{ + u16 boot_swapboot = 0; /* default value */ + u32 mem_staddr; + + /* MEM_STNDCTL: disable ints, disable nand boot */ + au_writel(0, MEM_STNDCTL); + +#ifdef CONFIG_MIPS_PB1550 + /* set gpio206 high */ + gpio_set_value(6, 1); + + boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr->status >> 6) & 0x1); + switch (boot_swapboot) { + case 0: + case 2: + case 8: + case 0xC: + case 0xD: + /* x16 NAND Flash */ + nand_width = 0; + break; + case 1: + case 9: + case 3: + case 0xE: + case 0xF: + /* x8 NAND Flash */ + nand_width = 1; + break; + default: + printk(KERN_ERR "Pb1550 NAND: bad boot:swap\n"); + return -EINVAL; + } +#endif + + /* Configure chip-select; normally done by boot code, e.g. YAMON */ +#ifdef NAND_STCFG + if (NAND_CS == 0) { + au_writel(NAND_STCFG, MEM_STCFG0); + au_writel(NAND_STTIME, MEM_STTIME0); + au_writel(NAND_STADDR, MEM_STADDR0); + } + if (NAND_CS == 1) { + au_writel(NAND_STCFG, MEM_STCFG1); + au_writel(NAND_STTIME, MEM_STTIME1); + au_writel(NAND_STADDR, MEM_STADDR1); + } + if (NAND_CS == 2) { + au_writel(NAND_STCFG, MEM_STCFG2); + au_writel(NAND_STTIME, MEM_STTIME2); + au_writel(NAND_STADDR, MEM_STADDR2); + } + if (NAND_CS == 3) { + au_writel(NAND_STCFG, MEM_STCFG3); + au_writel(NAND_STTIME, MEM_STTIME3); + au_writel(NAND_STADDR, MEM_STADDR3); + } +#endif + + /* Locate NAND chip-select in order to determine NAND phys address */ + mem_staddr = 0x00000000; + if (((au_readl(MEM_STCFG0) & 0x7) == 0x5) && (NAND_CS == 0)) + mem_staddr = au_readl(MEM_STADDR0); + else if (((au_readl(MEM_STCFG1) & 0x7) == 0x5) && (NAND_CS == 1)) + mem_staddr = au_readl(MEM_STADDR1); + else if (((au_readl(MEM_STCFG2) & 0x7) == 0x5) && (NAND_CS == 2)) + mem_staddr = au_readl(MEM_STADDR2); + else if (((au_readl(MEM_STCFG3) & 0x7) == 0x5) && (NAND_CS == 3)) + mem_staddr = au_readl(MEM_STADDR3); + + if (mem_staddr == 0x00000000) { + printk(KERN_ERR "Au1xxx NAND: ERROR WITH NAND CHIP-SELECT\n"); + return 1; + } + au1xxx_nand_slot[0].start = (mem_staddr << 4) & 0xFFFC0000; + au1xxx_nand_slot[0].end = au1xxx_nand_slot[0].start + 0x1000; + + /* make controller and MTD agree */ + if (NAND_CS == 0) + nand_width = au_readl(MEM_STCFG0) & (1 << 22); + if (NAND_CS == 1) + nand_width = au_readl(MEM_STCFG1) & (1 << 22); + if (NAND_CS == 2) + nand_width = au_readl(MEM_STCFG2) & (1 << 22); + if (NAND_CS == 3) + nand_width = au_readl(MEM_STCFG3) & (1 << 22); + + if (!nand_width) + au1xxx_nand_data.chip.options |= NAND_BUSWIDTH_16; + + au1xxx_nand_data.chip.read_byte = (!nand_width) ? au_read_byte16 : au_read_byte; + au1550_write_byte = (!nand_width) ? au_write_byte16 : au_write_byte; + au1xxx_nand_data.chip.read_word = au_read_word; + au1xxx_nand_data.chip.write_buf = (!nand_width) ? au_write_buf16 : au_write_buf; + au1xxx_nand_data.chip.read_buf = (!nand_width) ? au_read_buf16 : au_read_buf; + au1xxx_nand_data.chip.verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf; + + return platform_device_register(&au1xxx_nand_device); +} + +module_init(au1xxx_nand_init);