All pastes #739712 Raw Edit

Anonymous

public text v1 · immutable
·published 2007-10-17 12:32 UTC
rendered paste body
--- 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 <florian@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/version.h>
+#include <asm/io.h>
+
+#include <asm/gpio.h>
+#include <asm/mach-au1x00/au1xxx.h>
+
+
+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);