summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorRob Herring <r.herring@freescale.com>2009-10-19 14:43:19 -0500
committerAlejandro Gonzalez <alex.gonzalez@digi.com>2010-02-12 17:19:16 +0100
commitcdde68e3a7d4cbf4701005ab6032366e76009419 (patch)
tree5690552665f0b7843e6552e4d5fe7b63cbc78f51 /drivers/mtd
parent57d1417ea543b83760b3fd76a46b9d29deb2e444 (diff)
ENGR00117389 Port 5.0.0 release to 2.6.31
This is i.MX BSP 5.0.0 release ported to 2.6.31 Signed-off-by: Rob Herring <r.herring@freescale.com> Signed-off-by: Alan Tull <r80115@freescale.com> Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/mxc_dataflash.c1031
-rw-r--r--drivers/mtd/maps/Kconfig10
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/mxc_nor.c184
-rw-r--r--drivers/mtd/nand/Kconfig96
-rw-r--r--drivers/mtd/nand/Makefile5
-rw-r--r--drivers/mtd/nand/gpmi/Makefile6
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-base.c1976
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-bbt.c565
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-bch.c293
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-ecc8.c387
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-hamming-13-8.c130
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c195
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-hamming-22-16.h43
-rw-r--r--drivers/mtd/nand/gpmi/gpmi.h293
-rw-r--r--drivers/mtd/nand/imx_nfc.c8286
-rw-r--r--drivers/mtd/nand/lba/Makefile2
-rw-r--r--drivers/mtd/nand/lba/gpmi-transport.c832
-rw-r--r--drivers/mtd/nand/lba/gpmi.h103
-rw-r--r--drivers/mtd/nand/lba/lba-blk.c345
-rw-r--r--drivers/mtd/nand/lba/lba-core.c619
-rw-r--r--drivers/mtd/nand/lba/lba.h140
-rw-r--r--drivers/mtd/nand/mxc_nd.c1413
-rw-r--r--drivers/mtd/nand/mxc_nd.h112
-rw-r--r--drivers/mtd/nand/mxc_nd2.c1448
-rw-r--r--drivers/mtd/nand/mxc_nd2.h679
27 files changed, 19195 insertions, 0 deletions
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 0993d5cf3923..f008baccd796 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
+
diff --git a/drivers/mtd/devices/mxc_dataflash.c b/drivers/mtd/devices/mxc_dataflash.c
new file mode 100644
index 000000000000..88c5788cb445
--- /dev/null
+++ b/drivers/mtd/devices/mxc_dataflash.c
@@ -0,0 +1,1031 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * (c) 2005 MontaVista Software, Inc.
+ *
+ * This code is based on mtd_dataflash.c by adding FSL spi access.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+/*
+ * DataFlash is a kind of SPI flash. Most AT45 chips have two buffers in
+ * each chip, which may be used for double buffered I/O; but this driver
+ * doesn't (yet) use these for any kind of i/o overlap or prefetching.
+ *
+ * Sometimes DataFlash is packaged in MMC-format cards, although the
+ * MMC stack can't (yet?) distinguish between MMC and DataFlash
+ * protocols during enumeration.
+ */
+
+/* reads can bypass the buffers */
+#define OP_READ_CONTINUOUS 0xE8
+#define OP_READ_PAGE 0xD2
+
+/* group B requests can run even while status reports "busy" */
+#define OP_READ_STATUS 0xD7 /* group B */
+
+/* move data between host and buffer */
+#define OP_READ_BUFFER1 0xD4 /* group B */
+#define OP_READ_BUFFER2 0xD6 /* group B */
+#define OP_WRITE_BUFFER1 0x84 /* group B */
+#define OP_WRITE_BUFFER2 0x87 /* group B */
+
+/* erasing flash */
+#define OP_ERASE_PAGE 0x81
+#define OP_ERASE_BLOCK 0x50
+
+/* move data between buffer and flash */
+#define OP_TRANSFER_BUF1 0x53
+#define OP_TRANSFER_BUF2 0x55
+#define OP_MREAD_BUFFER1 0xD4
+#define OP_MREAD_BUFFER2 0xD6
+#define OP_MWERASE_BUFFER1 0x83
+#define OP_MWERASE_BUFFER2 0x86
+#define OP_MWRITE_BUFFER1 0x88 /* sector must be pre-erased */
+#define OP_MWRITE_BUFFER2 0x89 /* sector must be pre-erased */
+
+/* write to buffer, then write-erase to flash */
+#define OP_PROGRAM_VIA_BUF1 0x82
+#define OP_PROGRAM_VIA_BUF2 0x85
+
+/* compare buffer to flash */
+#define OP_COMPARE_BUF1 0x60
+#define OP_COMPARE_BUF2 0x61
+
+/* read flash to buffer, then write-erase to flash */
+#define OP_REWRITE_VIA_BUF1 0x58
+#define OP_REWRITE_VIA_BUF2 0x59
+
+/* newer chips report JEDEC manufacturer and device IDs; chip
+ * serial number and OTP bits; and per-sector writeprotect.
+ */
+#define OP_READ_ID 0x9F
+#define OP_READ_SECURITY 0x77
+#define OP_WRITE_SECURITY_REVC 0x9A
+#define OP_WRITE_SECURITY 0x9B /* revision D */
+
+#define SPI_FIFOSIZE 24 /* Bust size in bytes */
+#define CMD_SIZE 4
+#define DUMY_SIZE 4
+
+struct dataflash {
+ uint8_t command[4];
+ char name[24];
+
+ unsigned partitioned:1;
+
+ unsigned short page_offset; /* offset in flash address */
+ unsigned int page_size; /* of bytes per page */
+
+ struct mutex lock;
+ struct spi_device *spi;
+
+ struct mtd_info mtd;
+};
+
+#ifdef CONFIG_MTD_PARTITIONS
+#define mtd_has_partitions() (1)
+#else
+#define mtd_has_partitions() (0)
+#endif
+
+/* ......................................................................... */
+
+/*
+ * This function initializes the SPI device parameters.
+ */
+static inline int spi_nor_setup(struct spi_device *spi, u8 bst_len)
+{
+ spi->bits_per_word = bst_len << 3;
+
+ return spi_setup(spi);
+}
+
+/*
+ * This function perform spi read/write transfer.
+ */
+static int spi_read_write(struct spi_device *spi, u8 * buf, u32 len)
+{
+ struct spi_message m;
+ struct spi_transfer t;
+
+ if (len > SPI_FIFOSIZE || len <= 0)
+ return -1;
+
+ spi_nor_setup(spi, len);
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof t);
+
+ t.tx_buf = buf;
+ t.rx_buf = buf;
+ t.len = ((len - 1) >> 2) + 1;
+
+ spi_message_add_tail(&t, &m);
+
+ if (spi_sync(spi, &m) != 0 || m.status != 0) {
+ printk(KERN_ERR "%s: error\n", __func__);
+ return -1;
+ }
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: len: 0x%x success\n", __func__, len);
+
+ return 0;
+
+}
+
+/*
+ * Return the status of the DataFlash device.
+ */
+static inline int dataflash_status(struct spi_device *spi)
+{
+ /* NOTE: at45db321c over 25 MHz wants to write
+ * a dummy byte after the opcode...
+ */
+ ssize_t retval;
+
+ u16 val = OP_READ_STATUS << 8;
+
+ retval = spi_read_write(spi, (u8 *)&val, 2);
+
+ if (retval < 0)
+ return retval;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: status: 0x%x\n", __func__, val & 0xff);
+
+ return val & 0xff;
+}
+
+/*
+ * Poll the DataFlash device until it is READY.
+ * This usually takes 5-20 msec or so; more for sector erase.
+ */
+static int dataflash_waitready(struct spi_device *spi)
+{
+ int status;
+
+ for (;;) {
+ status = dataflash_status(spi);
+ if (status < 0) {
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: status %d?\n",
+ dev_name(&spi->dev), status);
+ status = 0;
+ }
+
+ if (status & (1 << 7)) /* RDY/nBSY */
+ return status;
+
+ msleep(3);
+ }
+}
+
+/* ......................................................................... */
+
+/*
+ * Erase pages of flash.
+ */
+static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct dataflash *priv = (struct dataflash *)mtd->priv;
+ struct spi_device *spi = priv->spi;
+ unsigned blocksize = priv->page_size << 3;
+ uint8_t *command;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n",
+ dev_name(&spi->dev), instr->addr, instr->len);
+
+ /* Sanity checks */
+ if ((instr->addr + instr->len) > mtd->size
+ || (instr->len % priv->page_size) != 0
+ || (instr->addr % priv->page_size) != 0)
+ return -EINVAL;
+
+ command = priv->command;
+
+ mutex_lock(&priv->lock);
+ while (instr->len > 0) {
+ unsigned int pageaddr;
+ int status;
+ int do_block;
+
+ /* Calculate flash page address; use block erase (for speed) if
+ * we're at a block boundary and need to erase the whole block.
+ */
+ pageaddr = instr->addr / priv->page_size;
+ do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize;
+ pageaddr = pageaddr << priv->page_offset;
+
+ command[3] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
+ command[2] = (uint8_t) (pageaddr >> 16);
+ command[1] = (uint8_t) (pageaddr >> 8);
+ command[0] = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL3, "ERASE %s: (%x) %x %x %x [%i]\n",
+ do_block ? "block" : "page",
+ command[0], command[1], command[2], command[3], pageaddr);
+
+ status = spi_read_write(spi, command, 4);
+ (void)dataflash_waitready(spi);
+
+ if (status < 0) {
+ printk(KERN_ERR "%s: erase %x, err %d\n",
+ dev_name(&spi->dev), pageaddr, status);
+ /* REVISIT: can retry instr->retries times; or
+ * giveup and instr->fail_addr = instr->addr;
+ */
+ continue;
+ }
+
+ if (do_block) {
+ instr->addr += blocksize;
+ instr->len -= blocksize;
+ } else {
+ instr->addr += priv->page_size;
+ instr->len -= priv->page_size;
+ }
+ }
+ mutex_unlock(&priv->lock);
+
+ /* Inform MTD subsystem that erase is complete */
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+}
+
+/*
+ * Read from the DataFlash device.
+ * from : Start offset in flash device
+ * len : Amount to read
+ * retlen : About of data actually read
+ * buf : Buffer containing the data
+ */
+static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct dataflash *priv = mtd->priv;
+ struct spi_device *spi = priv->spi;
+ u32 addr;
+ int rx_len = 0, count = 0, i = 0;
+ u_char txer[SPI_FIFOSIZE];
+ u_char *s = txer;
+ u_char *d = buf;
+ int cmd_len = CMD_SIZE + DUMY_SIZE;
+ int status = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: read 0x%x..0x%x\n",
+ dev_name(&priv->spi->dev), (unsigned)from, (unsigned)(from + len));
+
+ *retlen = 0;
+
+ /* Sanity checks */
+ if (!len)
+ return 0;
+
+ if (from + len > mtd->size)
+ return -EINVAL;
+
+ /* Calculate flash page/byte address */
+ addr = (((unsigned)from / priv->page_size) << priv->page_offset)
+ + ((unsigned)from % priv->page_size);
+
+ mutex_unlock(&priv->lock);
+
+ while (len > 0) {
+
+ rx_len = len > (SPI_FIFOSIZE - cmd_len) ?
+ SPI_FIFOSIZE - cmd_len : len;
+
+ txer[3] = OP_READ_CONTINUOUS;
+ txer[2] = (addr >> 16) & 0xff;
+ txer[1] = (addr >> 8) & 0xff;
+ txer[0] = addr & 0xff;
+
+ status = spi_read_write(spi, txer,
+ roundup(rx_len, 4) + cmd_len);
+ if (status) {
+ mutex_unlock(&priv->lock);
+ return status;
+ }
+
+ s = txer + cmd_len;
+
+ for (i = rx_len; i >= 0; i -= 4, s += 4) {
+ if (i < 4) {
+ if (i == 1) {
+ *d = s[3];
+ } else if (i == 2) {
+ *d++ = s[3];
+ *d++ = s[2];
+ } else if (i == 3) {
+ *d++ = s[3];
+ *d++ = s[2];
+ *d++ = s[1];
+ }
+
+ break;
+ }
+
+ *d++ = s[3];
+ *d++ = s[2];
+ *d++ = s[1];
+ *d++ = s[0];
+ }
+
+ /* updaate */
+ len -= rx_len;
+ addr += rx_len;
+ count += rx_len;
+
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "%s: left:0x%x, from:0x%08x, to:0x%p, done: 0x%x\n",
+ __func__, len, (u32) addr, d, count);
+ }
+
+ *retlen = count;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: %d bytes read\n", __func__, count);
+
+ mutex_unlock(&priv->lock);
+
+ return status;
+}
+
+/*
+ * Write to the DataFlash device.
+ * to : Start offset in flash device
+ * len : Amount to write
+ * retlen : Amount of data actually written
+ * buf : Buffer containing the data
+ */
+static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct dataflash *priv = mtd->priv;
+ struct spi_device *spi = priv->spi;
+ u32 pageaddr, addr, offset, writelen;
+ size_t remaining = len;
+ u_char *writebuf = (u_char *) buf;
+ int status = -EINVAL;
+ u_char txer[SPI_FIFOSIZE] = { 0 };
+ uint8_t *command = priv->command;
+ u_char *d = txer;
+ u_char *s = (u_char *) buf;
+ int delta = 0, l = 0, i = 0, count = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: write 0x%x..0x%x\n",
+ dev_name(&spi->dev), (unsigned)to, (unsigned)(to + len));
+
+ *retlen = 0;
+
+ /* Sanity checks */
+ if (!len)
+ return 0;
+
+ if ((to + len) > mtd->size)
+ return -EINVAL;
+
+ pageaddr = ((unsigned)to / priv->page_size);
+ offset = ((unsigned)to % priv->page_size);
+ if (offset + len > priv->page_size)
+ writelen = priv->page_size - offset;
+ else
+ writelen = len;
+
+ mutex_lock(&priv->lock);
+
+ while (remaining > 0) {
+ DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n",
+ pageaddr, offset, writelen);
+
+ addr = pageaddr << priv->page_offset;
+
+ /* (1) Maybe transfer partial page to Buffer1 */
+ if (writelen != priv->page_size) {
+ command[3] = OP_TRANSFER_BUF1;
+ command[2] = (addr & 0x00FF0000) >> 16;
+ command[1] = (addr & 0x0000FF00) >> 8;
+ command[0] = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL3, "TRANSFER: (%x) %x %x %x\n",
+ command[3], command[2], command[1], command[0]);
+
+ status = spi_read_write(spi, command, CMD_SIZE);
+ if (status) {
+ mutex_unlock(&priv->lock);
+ return status;
+
+ }
+
+ (void)dataflash_waitready(spi);
+ }
+
+ count = writelen;
+ while (count) {
+ d = txer;
+ l = count > (SPI_FIFOSIZE - CMD_SIZE) ?
+ SPI_FIFOSIZE - CMD_SIZE : count;
+
+ delta = l % 4;
+ if (delta) {
+ switch (delta) {
+ case 1:
+ d[0] = OP_WRITE_BUFFER1;
+ d[6] = (offset >> 8) & 0xff;
+ d[5] = offset & 0xff;
+ d[4] = *s++;
+ break;
+ case 2:
+ d[1] = OP_WRITE_BUFFER1;
+ d[7] = (offset >> 8) & 0xff;
+ d[6] = offset & 0xff;
+ d[5] = *s++;
+ d[4] = *s++;
+ break;
+ case 3:
+ d[2] = OP_WRITE_BUFFER1;
+ d[0] = (offset >> 8) & 0xff;
+ d[7] = offset & 0xff;
+ d[6] = *s++;
+ d[5] = *s++;
+ d[4] = *s++;
+ break;
+ default:
+ break;
+ }
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "WRITEBUF: (%x) %x %x %x\n",
+ txer[3], txer[2], txer[1], txer[0]);
+
+ status = spi_read_write(spi, txer,
+ delta + CMD_SIZE);
+ if (status) {
+ mutex_unlock(&priv->lock);
+ return status;
+ }
+
+ /* update */
+ count -= delta;
+ offset += delta;
+ l -= delta;
+ }
+
+ d[3] = OP_WRITE_BUFFER1;
+ d[1] = (offset >> 8) & 0xff;
+ d[0] = offset & 0xff;
+
+ for (i = 0, d += 4; i < l / 4; i++, d += 4) {
+ d[3] = *s++;
+ d[2] = *s++;
+ d[1] = *s++;
+ d[0] = *s++;
+ }
+
+ DEBUG(MTD_DEBUG_LEVEL3, "WRITEBUF: (%x) %x %x %x\n",
+ txer[3], txer[2], txer[1], txer[0]);
+
+ status = spi_read_write(spi, txer, l + CMD_SIZE);
+ if (status) {
+ mutex_unlock(&priv->lock);
+ return status;
+ }
+
+ /* update */
+ count -= l;
+ offset += l;
+ }
+
+ /* (2) Program full page via Buffer1 */
+ command[3] = OP_MWERASE_BUFFER1;
+ command[2] = (addr >> 16) & 0xff;
+ command[1] = (addr >> 8) & 0xff;
+
+ DEBUG(MTD_DEBUG_LEVEL3, "PROGRAM: (%x) %x %x %x\n",
+ command[3], command[2], command[1], command[0]);
+
+ status = spi_read_write(spi, command, CMD_SIZE);
+ if (status) {
+ mutex_unlock(&priv->lock);
+ return status;
+ }
+
+ (void)dataflash_waitready(spi);
+
+ remaining -= writelen;
+ pageaddr++;
+ offset = 0;
+ writebuf += writelen;
+ *retlen += writelen;
+
+ if (remaining > priv->page_size)
+ writelen = priv->page_size;
+ else
+ writelen = remaining;
+ }
+ mutex_unlock(&priv->lock);
+
+ return status;
+}
+
+/* ......................................................................... */
+
+#ifdef CONFIG_MTD_DATAFLASH_OTP
+
+static int dataflash_get_otp_info(struct mtd_info *mtd,
+ struct otp_info *info, size_t len)
+{
+ /* Report both blocks as identical: bytes 0..64, locked.
+ * Unless the user block changed from all-ones, we can't
+ * tell whether it's still writable; so we assume it isn't.
+ */
+ info->start = 0;
+ info->length = 64;
+ info->locked = 1;
+ return sizeof(*info);
+}
+
+static ssize_t otp_read(struct spi_device *spi, unsigned base,
+ uint8_t *buf, loff_t off, size_t len)
+{
+ struct dataflash *priv = mtd->priv;
+ struct spi_device *spi = priv->spi;
+ int rx_len = 0, count = 0, i = 0;
+ u_char txer[SPI_FIFOSIZE];
+ u_char *s = txer;
+ u_char *d = NULL;
+ int cmd_len = CMD_SIZE;
+ int status;
+
+ if (off > 64)
+ return -EINVAL;
+
+ if ((off + len) > 64)
+ len = 64 - off;
+ if (len == 0)
+ return len;
+
+ /* to make simple, we read 64 out */
+ l = base + 64;
+
+ d = kzalloc(l, GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ while (l > 0) {
+
+ rx_len = l > (SPI_FIFOSIZE - cmd_len) ?
+ SPI_FIFOSIZE - cmd_len : l;
+
+ txer[3] = OP_READ_SECURITY;
+
+ status = spi_read_write(spi, txer, rx_len + cmd_len);
+ if (status) {
+ mutex_unlock(&priv->lock);
+ return status;
+ }
+
+ s = txer + cmd_len;
+ for (i = rx_len; i >= 0; i -= 4, s += 4) {
+
+ *d++ = s[3];
+ *d++ = s[2];
+ *d++ = s[1];
+ *d++ = s[0];
+ }
+
+ /* updaate */
+ l -= rx_len;
+ addr += rx_len;
+ count += rx_len;
+
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "%s: left:0x%x, from:0x%08x, to:0x%p, done: 0x%x\n",
+ __func__, len, (u32) addr, d, count);
+ }
+
+ d -= count;
+ memcpy(buf, d + base + off, len);
+
+ mutex_unlock(&priv->lock);
+
+ return len;
+}
+
+static int dataflash_read_fact_otp(struct mtd_info *mtd,
+ loff_t from, size_t len, size_t *retlen,
+ u_char *buf)
+{
+ struct dataflash *priv = (struct dataflash *)mtd->priv;
+ int status;
+
+ /* 64 bytes, from 0..63 ... start at 64 on-chip */
+ mutex_lock(&priv->lock);
+ status = otp_read(priv->spi, 64, buf, from, len);
+ mutex_unlock(&priv->lock);
+
+ if (status < 0)
+ return status;
+ *retlen = status;
+ return 0;
+}
+
+static int dataflash_read_user_otp(struct mtd_info *mtd,
+ loff_t from, size_t len, size_t *retlen,
+ u_char *buf)
+{
+ struct dataflash *priv = (struct dataflash *)mtd->priv;
+ int status;
+
+ /* 64 bytes, from 0..63 ... start at 0 on-chip */
+ mutex_lock(&priv->lock);
+ status = otp_read(priv->spi, 0, buf, from, len);
+ mutex_unlock(&priv->lock);
+
+ if (status < 0)
+ return status;
+ *retlen = status;
+ return 0;
+}
+
+static int dataflash_write_user_otp(struct mtd_info *mtd,
+ loff_t from, size_t len, size_t *retlen,
+ u_char *buf)
+{
+ printk(KERN_ERR "%s not support!!\n", __func__);
+ return 0;
+}
+
+static char *otp_setup(struct mtd_info *device, char revision)
+{
+ device->get_fact_prot_info = dataflash_get_otp_info;
+ device->read_fact_prot_reg = dataflash_read_fact_otp;
+ device->get_user_prot_info = dataflash_get_otp_info;
+ device->read_user_prot_reg = dataflash_read_user_otp;
+
+ /* rev c parts (at45db321c and at45db1281 only!) use a
+ * different write procedure; not (yet?) implemented.
+ */
+ if (revision > 'c')
+ device->write_user_prot_reg = dataflash_write_user_otp;
+
+ return ", OTP";
+}
+
+#else
+
+static char *otp_setup(struct mtd_info *device, char revision)
+{
+ return " (OTP)";
+}
+
+#endif
+
+/* ......................................................................... */
+
+/*
+ * Register DataFlash device with MTD subsystem.
+ */
+static int __devinit
+add_dataflash_otp(struct spi_device *spi, char *name,
+ int nr_pages, int pagesize, int pageoffset, char revision)
+{
+ struct dataflash *priv;
+ struct mtd_info *device;
+ struct flash_platform_data *pdata = spi->dev.platform_data;
+ char *otp_tag = "";
+
+ priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+ priv->spi = spi;
+ priv->page_size = pagesize;
+ priv->page_offset = pageoffset;
+
+ /* name must be usable with cmdlinepart */
+ sprintf(priv->name, "spi%d.%d-%s",
+ spi->master->bus_num, spi->chip_select, name);
+
+ device = &priv->mtd;
+ device->name = (pdata && pdata->name) ? pdata->name : priv->name;
+ device->size = nr_pages * pagesize;
+ device->erasesize = pagesize;
+ device->writesize = pagesize;
+ device->owner = THIS_MODULE;
+ device->type = MTD_DATAFLASH;
+ device->flags = MTD_CAP_NORFLASH;
+ device->erase = dataflash_erase;
+ device->read = dataflash_read;
+ device->write = dataflash_write;
+ device->priv = priv;
+
+ if (revision >= 'c')
+ otp_tag = otp_setup(device, revision);
+
+ dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes%s\n",
+ name, DIV_ROUND_UP(device->size, 1024), pagesize, otp_tag);
+ dev_set_drvdata(&spi->dev, priv);
+
+ if (mtd_has_partitions()) {
+ struct mtd_partition *parts;
+ int nr_parts = 0;
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ static const char *part_probes[] = { "cmdlinepart", NULL, };
+
+ nr_parts = parse_mtd_partitions(device, part_probes, &parts, 0);
+#endif
+
+ if (nr_parts <= 0 && pdata && pdata->parts) {
+ parts = pdata->parts;
+ nr_parts = pdata->nr_parts;
+ }
+
+ if (nr_parts > 0) {
+ priv->partitioned = 1;
+ return add_mtd_partitions(device, parts, nr_parts);
+ }
+ } else if (pdata && pdata->nr_parts)
+ dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
+ pdata->nr_parts, device->name);
+
+ return add_mtd_device(device) == 1 ? -ENODEV : 0;
+}
+
+static inline int __devinit
+add_dataflash(struct spi_device *spi, char *name,
+ int nr_pages, int pagesize, int pageoffset)
+{
+ return add_dataflash_otp(spi, name, nr_pages, pagesize, pageoffset, 0);
+}
+
+struct flash_info {
+ char *name;
+
+ /* JEDEC id has a high byte of zero plus three data bytes:
+ * the manufacturer id, then a two byte device id.
+ */
+ uint32_t jedec_id;
+
+ /* The size listed here is what works with OP_ERASE_PAGE. */
+ unsigned nr_pages;
+ uint16_t pagesize;
+ uint16_t pageoffset;
+
+ uint16_t flags;
+#define SUP_POW2PS 0x0002 /* supports 2^N byte pages */
+#define IS_POW2PS 0x0001 /* uses 2^N byte pages */
+};
+
+static struct flash_info __devinitdata dataflash_data[] = {
+
+ /*
+ * NOTE: chips with SUP_POW2PS (rev D and up) need two entries,
+ * one with IS_POW2PS and the other without. The entry with the
+ * non-2^N byte page size can't name exact chip revisions without
+ * losing backwards compatibility for cmdlinepart.
+ *
+ * These newer chips also support 128-byte security registers (with
+ * 64 bytes one-time-programmable) and software write-protection.
+ */
+ {"AT45DB011B", 0x1f2200, 512, 264, 9, SUP_POW2PS},
+ {"at45db011d", 0x1f2200, 512, 256, 8, SUP_POW2PS | IS_POW2PS},
+
+ {"AT45DB021B", 0x1f2300, 1024, 264, 9, SUP_POW2PS},
+ {"at45db021d", 0x1f2300, 1024, 256, 8, SUP_POW2PS | IS_POW2PS},
+
+ {"AT45DB041x", 0x1f2400, 2048, 264, 9, SUP_POW2PS},
+ {"at45db041d", 0x1f2400, 2048, 256, 8, SUP_POW2PS | IS_POW2PS},
+
+ {"AT45DB081B", 0x1f2500, 4096, 264, 9, SUP_POW2PS},
+ {"at45db081d", 0x1f2500, 4096, 256, 8, SUP_POW2PS | IS_POW2PS},
+
+ {"AT45DB161x", 0x1f2600, 4096, 528, 10, SUP_POW2PS},
+ {"at45db161d", 0x1f2600, 4096, 512, 9, SUP_POW2PS | IS_POW2PS},
+
+ {"AT45DB321x", 0x1f2700, 8192, 528, 10, 0}, /* rev C */
+
+ {"AT45DB321x", 0x1f2701, 8192, 528, 10, SUP_POW2PS},
+ {"at45db321d", 0x1f2701, 8192, 512, 9, SUP_POW2PS | IS_POW2PS},
+
+ {"AT45DB642x", 0x1f2800, 8192, 1056, 11, SUP_POW2PS},
+ {"at45db642d", 0x1f2800, 8192, 1024, 10, SUP_POW2PS | IS_POW2PS},
+};
+
+static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+{
+ int tmp;
+ u32 code = OP_READ_ID << 24;
+ u32 jedec;
+ struct flash_info *info;
+ int status;
+
+ /* JEDEC also defines an optional "extended device information"
+ * string for after vendor-specific data, after the three bytes
+ * we use here. Supporting some chips might require using it.
+ *
+ * If the vendor ID isn't Atmel's (0x1f), assume this call failed.
+ * That's not an error; only rev C and newer chips handle it, and
+ * only Atmel sells these chips.
+ */
+
+ tmp = spi_read_write(spi, (u8 *)&code, 4);
+ if (tmp < 0) {
+ DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
+ dev_name(&spi->dev), tmp);
+ return NULL;
+ }
+
+ jedec = code & 0xFFFFFF;
+
+ for (tmp = 0, info = dataflash_data;
+ tmp < ARRAY_SIZE(dataflash_data); tmp++, info++) {
+ if (info->jedec_id == jedec) {
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: OTP, sector protect%s\n",
+ dev_name(&spi->dev), (info->flags & SUP_POW2PS)
+ ? ", binary pagesize" : "");
+ if (info->flags & SUP_POW2PS) {
+ status = dataflash_status(spi);
+ if (status < 0) {
+ DEBUG(MTD_DEBUG_LEVEL1,
+ "%s: status error %d\n",
+ dev_name(&spi->dev), status);
+ return ERR_PTR(status);
+ }
+ if (status & 0x1) {
+ if (info->flags & IS_POW2PS)
+ return info;
+ } else {
+ if (!(info->flags & IS_POW2PS))
+ return info;
+ }
+ }
+ }
+ }
+
+ /*
+ * Treat other chips as errors ... we won't know the right page
+ * size (it might be binary) even when we can tell which density
+ * class is involved (legacy chip id scheme).
+ */
+ dev_warn(&spi->dev, "JEDEC id %06x not handled\n", jedec);
+ return ERR_PTR(-ENODEV);
+}
+
+/*
+ * Detect and initialize DataFlash device, using JEDEC IDs on newer chips
+ * or else the ID code embedded in the status bits:
+ *
+ * Device Density ID code #Pages PageSize Offset
+ * AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9
+ * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1024 264 9
+ * AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9
+ * AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9
+ * AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10
+ * AT45DB0321B 32Mbit (4M) xx1101xx (0x34) 8192 528 10
+ * AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11
+ * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11
+ */
+static int __devinit dataflash_probe(struct spi_device *spi)
+{
+ int status;
+ struct flash_info *info;
+
+ /*
+ * Try to detect dataflash by JEDEC ID.
+ * If it succeeds we know we have either a C or D part.
+ * D will support power of 2 pagesize option.
+ * Both support the security register, though with different
+ * write procedures.
+ */
+ info = jedec_probe(spi);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+ if (info != NULL)
+ return add_dataflash_otp(spi, info->name, info->nr_pages,
+ info->pagesize, info->pageoffset,
+ (info->flags & SUP_POW2PS) ? 'd' :
+ 'c');
+
+ /*
+ * Older chips support only legacy commands, identifing
+ * capacity using bits in the status byte.
+ */
+ status = dataflash_status(spi);
+ if (status <= 0 || status == 0xff) {
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: status error %d\n",
+ dev_name(&spi->dev), status);
+ if (status == 0 || status == 0xff)
+ status = -ENODEV;
+ return status;
+ }
+
+ /* if there's a device there, assume it's dataflash.
+ * board setup should have set spi->max_speed_max to
+ * match f(car) for continuous reads, mode 0 or 3.
+ */
+ switch (status & 0x3c) {
+ case 0x0c: /* 0 0 1 1 x x */
+ status = add_dataflash(spi, "AT45DB011B", 512, 264, 9);
+ break;
+ case 0x14: /* 0 1 0 1 x x */
+ status = add_dataflash(spi, "AT45DB021B", 1024, 264, 9);
+ break;
+ case 0x1c: /* 0 1 1 1 x x */
+ status = add_dataflash(spi, "AT45DB041x", 2048, 264, 9);
+ break;
+ case 0x24: /* 1 0 0 1 x x */
+ status = add_dataflash(spi, "AT45DB081B", 4096, 264, 9);
+ break;
+ case 0x2c: /* 1 0 1 1 x x */
+ status = add_dataflash(spi, "AT45DB161x", 4096, 528, 10);
+ break;
+ case 0x34: /* 1 1 0 1 x x */
+ status = add_dataflash(spi, "AT45DB321x", 8192, 528, 10);
+ break;
+ case 0x38: /* 1 1 1 x x x */
+ case 0x3c:
+ status = add_dataflash(spi, "AT45DB642x", 8192, 1056, 11);
+ break;
+ /* obsolete AT45DB1282 not (yet?) supported */
+ default:
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: unsupported device (%x)\n",
+ dev_name(&spi->dev), status & 0x3c);
+ status = -ENODEV;
+ }
+
+ if (status < 0)
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: add_dataflash --> %d\n",
+ dev_name(&spi->dev), status);
+
+ return status;
+}
+
+static int __devexit dataflash_remove(struct spi_device *spi)
+{
+ struct dataflash *flash = dev_get_drvdata(&spi->dev);
+ int status;
+
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", dev_name(&spi->dev));
+
+ if (mtd_has_partitions() && flash->partitioned)
+ status = del_mtd_partitions(&flash->mtd);
+ else
+ status = del_mtd_device(&flash->mtd);
+ if (status == 0)
+ kfree(flash);
+ return status;
+}
+
+static struct spi_driver dataflash_driver = {
+ .driver = {
+ .name = "mxc_dataflash",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = dataflash_probe,
+ .remove = __devexit_p(dataflash_remove),
+
+ /* FIXME: investigate suspend and resume... */
+};
+
+static int __init dataflash_init(void)
+{
+ return spi_register_driver(&dataflash_driver);
+}
+
+module_init(dataflash_init);
+
+static void __exit dataflash_exit(void)
+{
+ spi_unregister_driver(&dataflash_driver);
+}
+
+module_exit(dataflash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MTD DataFlash driver");
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 7a58bd5522fd..c90fcb79af03 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -546,4 +546,14 @@ config MTD_VMU
To build this as a module select M here, the module will be called
vmu-flash.
+config MTD_MXC
+ bool "Map driver for Freescale MXC boards"
+ depends on MTD && ARCH_MXC
+ default y
+ select MTD_CFI
+ select MTD_PARTITIONS
+ help
+ This enables access to the flash chips on Freescale MXC based
+ platforms. If you have such a board, say 'Y'.
+
endmenu
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 5beb0662d724..01bf210825c9 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
+obj-$(CONFIG_MTD_MXC) += mxc_nor.o
diff --git a/drivers/mtd/maps/mxc_nor.c b/drivers/mtd/maps/mxc_nor.c
new file mode 100644
index 000000000000..69b55bcac847
--- /dev/null
+++ b/drivers/mtd/maps/mxc_nor.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * (c) 2005 MontaVista Software, Inc.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/clocksource.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
+
+#define DVR_VER "2.0"
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
+#endif
+
+struct clocksource *mtd_xip_clksrc;
+
+struct mxcflash_info {
+ struct mtd_partition *parts;
+ struct mtd_info *mtd;
+ struct map_info map;
+};
+
+/*!
+ * @defgroup NOR_MTD NOR Flash MTD Driver
+ */
+
+/*!
+ * @file mxc_nor.c
+ *
+ * @brief This file contains the MTD Mapping information on the MXC.
+ *
+ * @ingroup NOR_MTD
+ */
+
+static int __devinit mxcflash_probe(struct platform_device *pdev)
+{
+ int err, nr_parts = 0;
+ struct mxcflash_info *info;
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+ struct resource *res = pdev->resource;
+ unsigned long size = res->end - res->start + 1;
+
+ info = kzalloc(sizeof(struct mxcflash_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (!request_mem_region(res->start, size, "flash")) {
+ err = -EBUSY;
+ goto out_free_info;
+ }
+ info->map.virt = ioremap(res->start, size);
+ if (!info->map.virt) {
+ err = -ENOMEM;
+ goto out_release_mem_region;
+ }
+ info->map.name = dev_name(&pdev->dev);
+ info->map.phys = res->start;
+ info->map.size = size;
+ info->map.bankwidth = flash->width;
+
+ mtd_xip_clksrc = clocksource_get_next();
+
+ simple_map_init(&info->map);
+ info->mtd = do_map_probe(flash->map_name, &info->map);
+ if (!info->mtd) {
+ err = -EIO;
+ goto out_iounmap;
+ }
+ info->mtd->owner = THIS_MODULE;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ nr_parts =
+ parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0);
+ if (nr_parts > 0) {
+ add_mtd_partitions(info->mtd, info->parts, nr_parts);
+ } else if (flash->parts) {
+ add_mtd_partitions(info->mtd, flash->parts, flash->nr_parts);
+ } else
+#endif
+ {
+ printk(KERN_NOTICE "MXC flash: no partition info "
+ "available, registering whole flash\n");
+ add_mtd_device(info->mtd);
+ }
+
+ platform_set_drvdata(pdev, info);
+ return 0;
+
+ out_iounmap:
+ iounmap(info->map.virt);
+ out_release_mem_region:
+ release_mem_region(res->start, size);
+ out_free_info:
+ kfree(info);
+
+ return err;
+}
+
+static int __devexit mxcflash_remove(struct platform_device *pdev)
+{
+
+ struct mxcflash_info *info = platform_get_drvdata(pdev);
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (info) {
+ if (info->parts) {
+ del_mtd_partitions(info->mtd);
+ kfree(info->parts);
+ } else if (flash->parts)
+ del_mtd_partitions(info->mtd);
+ else
+ del_mtd_device(info->mtd);
+
+ map_destroy(info->mtd);
+ release_mem_region(info->map.phys, info->map.size);
+ iounmap((void __iomem *)info->map.virt);
+ kfree(info);
+ }
+ return 0;
+}
+
+static struct platform_driver mxcflash_driver = {
+ .driver = {
+ .name = "mxc_nor_flash",
+ },
+ .probe = mxcflash_probe,
+ .remove = __devexit_p(mxcflash_remove),
+};
+
+/*!
+ * This is the module's entry function. It passes board specific
+ * config details into the MTD physmap driver which then does the
+ * real work for us. After this function runs, our job is done.
+ *
+ * @return 0 if successful; non-zero otherwise
+ */
+static int __init mxc_mtd_init(void)
+{
+ pr_info("MXC MTD nor Driver %s\n", DVR_VER);
+ if (platform_driver_register(&mxcflash_driver) != 0) {
+ printk(KERN_ERR "Driver register failed for mxcflash_driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/*!
+ * This function is the module's exit function. It's empty because the
+ * MTD physmap driver is doing the real work and our job was done after
+ * mxc_mtd_init() runs.
+ */
+static void __exit mxc_mtd_exit(void)
+{
+ platform_driver_unregister(&mxcflash_driver);
+}
+
+module_init(mxc_mtd_init);
+module_exit(mxc_mtd_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MTD map and partitions for Freescale MXC boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index ce96c091f01b..9653aa5e8628 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -378,6 +378,102 @@ config MTD_NAND_NANDSIM
The simulator may simulate various NAND flash chips for the
MTD nand layer.
+config MTD_NAND_IMX_NFC
+ tristate "i.MX NAND Flash Controller driver"
+ depends on MTD_NAND
+ help
+ Enables the i.MX NAND Flash controller driver.
+
+config MTD_NAND_MXC
+ tristate "MXC NAND support"
+ depends on MTD_NAND && ARCH_MXC_HAS_NFC_V1
+ help
+ This enables the driver for the NAND flash controller on the
+ MXC processors.
+
+config MTD_NAND_MXC_V2
+ tristate "MXC NAND Version 2 support"
+ depends on MTD_NAND && ARCH_MXC_HAS_NFC_V2
+ help
+ This enables the driver for the version 2 of NAND flash controller
+ on the MXC processors.
+
+config MTD_NAND_MXC_V3
+ tristate "MXC NAND Version 3 support"
+ depends on MTD_NAND && ARCH_MXC_HAS_NFC_V3
+ help
+ This enables the driver for the version 3 of NAND flash controller
+ on the MXC processors.
+
+config MTD_NAND_MXC_SWECC
+ bool "Software ECC support "
+ depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 || MTD_NAND_MXC_V3
+ help
+ This enables the support for Software ECC handling. By
+ default MXC NAND controller Hardware ECC is supported.
+
+
+config MTD_NAND_MXC_FORCE_CE
+ bool "NAND chip select operation support"
+ depends on MTD_NAND_MXC || MTD_NAND_MXC_V2|| MTD_NAND_MXC_V3
+ help
+ This enables the NAND chip select by using CE control line. By
+ default CE operation is disabled.
+
+config MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ bool "ECC correction in S/W"
+ depends on MTD_NAND_MXC
+ help
+ This enables the Option2 NFC ECC correction in software. By
+ default Option 1 is selected. Enable if you need option2 ECC correction.
+
+config MXC_NAND_LOW_LEVEL_ERASE
+ bool "Low level NAND erase"
+ depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 || MTD_NAND_MXC_V3
+ help
+ This enables the erase of whole NAND flash. By
+ default low level erase operation is disabled.
+
+config MTD_NAND_GPMI_LBA
+ tristate "GPMI LBA NAND driver"
+ depends on MTD_NAND && ARCH_STMP3XXX
+ help
+ Enables support of LBA devices on GPMI on 37xx/378x SigmaTel
+ boards
+
+config MTD_NAND_GPMI
+ tristate "GPMI NAND driver"
+ depends on MTD_NAND && ARCH_STMP3XXX && !MTD_NAND_GPMI_LBA
+ help
+ Enables support of NAND devices on GPMI on 37xx/378x SigmaTel
+ boards
+
+config MTD_NAND_GPMI_SYSFS_ENTRIES
+ bool "Create /sys entries for GPMI device"
+ depends on MTD_NAND_GPMI
+ help
+ Check this to enable /sys entries for GPMI devices
+
+config MTD_NAND_GPMI_BCH
+ bool "Enable BCH HWECC"
+ depends on MTD_NAND_GPMI
+ depends on ARCH_STMP378X
+ default y
+ help
+ Check this to enable /sys entries for GPMI devices
+
+config MTD_NAND_GPMI_TA1
+ bool "Support for TA1 NCB format (Hamming code 22,16)"
+ depends on MTD_NAND_GPMI
+ depends on ARCH_STMP378X
+ default y
+
+config MTD_NAND_GPMI_TA3
+ bool "Support for TA3 NCB format (Hamming code 13,8)"
+ depends on MTD_NAND_GPMI
+ depends on ARCH_STMP378X
+ default y
+
config MTD_NAND_PLATFORM
tristate "Support for generic platform NAND driver"
depends on MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index f3a786b3cff3..f18e71f4a1eb 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -37,7 +37,12 @@ obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
+obj-$(CONFIG_MTD_NAND_IMX_NFC) += imx_nfc.o
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
+obj-$(CONFIG_MTD_NAND_MXC_V2) += mxc_nd2.o
+obj-$(CONFIG_MTD_NAND_MXC_V3) += mxc_nd2.o
+obj-$(CONFIG_MTD_NAND_GPMI) += gpmi/
+obj-$(CONFIG_MTD_NAND_GPMI_LBA) += lba/
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
diff --git a/drivers/mtd/nand/gpmi/Makefile b/drivers/mtd/nand/gpmi/Makefile
new file mode 100644
index 000000000000..4a4b50d294fa
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_MTD_NAND_GPMI) += gpmi.o
+gpmi-objs += gpmi-base.o gpmi-bbt.o
+gpmi-objs += gpmi-hamming-22-16.o
+gpmi-objs += gpmi-hamming-13-8.o
+gpmi-objs += gpmi-bch.o
+gpmi-objs += gpmi-ecc8.o
diff --git a/drivers/mtd/nand/gpmi/gpmi-base.c b/drivers/mtd/nand/gpmi/gpmi-base.c
new file mode 100644
index 000000000000..8bfb1c677a4e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-base.c
@@ -0,0 +1,1976 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/concat.h>
+#include <linux/dma-mapping.h>
+#include <linux/ctype.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <asm/div64.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/platform.h>
+#include <mach/regs-ecc8.h>
+#include <mach/regs-gpmi.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+
+static int debug;
+static int copies;
+static int map_buffers = true;
+static int ff_writes;
+static int raw_mode;
+static int add_mtd_entire;
+static int add_mtd_chip;
+static int ignorebad;
+static int max_chips = 4;
+static long clk = -1;
+static int bch /* = 0 */ ;
+
+static int gpmi_nand_init_hw(struct platform_device *pdev, int request_pins);
+static void gpmi_nand_release_hw(struct platform_device *pdev);
+static int gpmi_dma_exchange(struct gpmi_nand_data *g,
+ struct stmp3xxx_dma_descriptor *dma);
+static void gpmi_read_buf(struct mtd_info *mtd, uint8_t * buf, int len);
+
+struct gpmi_nand_timing gpmi_safe_timing = {
+ .address_setup = 25,
+ .data_setup = 80,
+ .data_hold = 60,
+ .dsample_time = 6,
+};
+
+/*
+ * define OOB placement schemes for 4k and 2k page devices
+ */
+static struct nand_ecclayout gpmi_oob_128 = {
+ .oobfree = {
+ {
+ .offset = 2,
+ .length = 56,
+ }, {
+ .length = 0,
+ },
+ },
+};
+
+static struct nand_ecclayout gpmi_oob_64 = {
+ .oobfree = {
+ {
+ .offset = 2,
+ .length = 16,
+ }, {
+ .length = 0,
+ },
+ },
+};
+
+static inline u32 gpmi_cycles_ceil(u32 ntime, u32 period)
+{
+ int k;
+
+ k = (ntime + period - 1) / period;
+ if (k == 0)
+ k++;
+ return k;
+}
+
+/**
+ * gpmi_timer_expiry - timer expiry handling
+ */
+static void gpmi_timer_expiry(unsigned long d)
+{
+#ifdef CONFIG_PM
+ struct gpmi_nand_data *g = (struct gpmi_nand_data *)d;
+
+ pr_debug("%s: timer expired\n", __func__);
+ del_timer_sync(&g->timer);
+
+ if (g->use_count ||
+ __raw_readl(REGS_GPMI_BASE + HW_GPMI_CTRL0) & BM_GPMI_CTRL0_RUN) {
+ g->timer.expires = jiffies + 4 * HZ;
+ add_timer(&g->timer);
+ } else {
+ stmp3xxx_setl(BM_GPMI_CTRL0_CLKGATE,
+ REGS_GPMI_BASE + HW_GPMI_CTRL0);
+ clk_disable(g->clk);
+ g->self_suspended = 1;
+ }
+#endif
+}
+
+/**
+ * gpmi_self_wakeup - wakeup from self-pm light suspend
+ */
+static void gpmi_self_wakeup(struct gpmi_nand_data *g)
+{
+#ifdef CONFIG_PM
+ int i = 1000;
+ clk_enable(g->clk);
+ stmp3xxx_clearl(BM_GPMI_CTRL0_CLKGATE, REGS_GPMI_BASE + HW_GPMI_CTRL0);
+ while (i--
+ && __raw_readl(REGS_GPMI_BASE +
+ HW_GPMI_CTRL0) & BM_GPMI_CTRL0_CLKGATE) ;
+
+ pr_debug("%s: i stopped at %d, data %p\n", __func__, i, g);
+ g->self_suspended = 0;
+ g->timer.expires = jiffies + 4 * HZ;
+ add_timer(&g->timer);
+#endif
+}
+
+/**
+ * gpmi_set_timings - set GPMI timings
+ * @pdev: pointer to GPMI platform device
+ * @tm: pointer to structure &gpmi_nand_timing with new timings
+ *
+ * During initialization, GPMI uses safe sub-optimal timings, which
+ * can be changed after reading boot control blocks
+ */
+void gpmi_set_timings(struct platform_device *pdev, struct gpmi_nand_timing *tm)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ u32 period_ns = 1000000 / clk_get_rate(g->clk) + 1;
+ u32 address_cycles, data_setup_cycles;
+ u32 data_hold_cycles, data_sample_cycles;
+ u32 busy_timeout;
+ u32 t0;
+
+ if (g->self_suspended)
+ gpmi_self_wakeup(g);
+ g->use_count++;
+
+ g->timing = *tm;
+
+ address_cycles = gpmi_cycles_ceil(tm->address_setup, period_ns);
+ data_setup_cycles = gpmi_cycles_ceil(tm->data_setup, period_ns);
+ data_hold_cycles = gpmi_cycles_ceil(tm->data_hold, period_ns);
+ data_sample_cycles = gpmi_cycles_ceil(tm->dsample_time + period_ns / 4,
+ period_ns / 2);
+ busy_timeout = gpmi_cycles_ceil(10000000 / 4096, period_ns);
+
+ dev_dbg(&pdev->dev,
+ "%s: ADDR %u, DSETUP %u, DH %u, DSAMPLE %u, BTO %u\n",
+ __func__,
+ address_cycles, data_setup_cycles, data_hold_cycles,
+ data_sample_cycles, busy_timeout);
+
+ t0 = BF(address_cycles, GPMI_TIMING0_ADDRESS_SETUP) |
+ BF(data_setup_cycles, GPMI_TIMING0_DATA_SETUP) |
+ BF(data_hold_cycles, GPMI_TIMING0_DATA_HOLD);
+ __raw_writel(t0, REGS_GPMI_BASE + HW_GPMI_TIMING0);
+
+ __raw_writel(BF(busy_timeout, GPMI_TIMING1_DEVICE_BUSY_TIMEOUT),
+ REGS_GPMI_BASE + HW_GPMI_TIMING1);
+
+#ifdef CONFIG_ARCH_STMP378X
+ stmp3xxx_clearl(BM_GPMI_CTRL1_RDN_DELAY,
+ REGS_GPMI_BASE + HW_GPMI_CTRL1);
+ stmp3xxx_setl(BF(data_sample_cycles, GPMI_CTRL1_RDN_DELAY),
+ REGS_GPMI_BASE + HW_GPMI_CTRL1);
+#else
+ stmp3xxx_clearl(BM_GPMI_CTRL1_DSAMPLE_TIME,
+ REGS_GPMI_BASE + HW_GPMI_CTRL1);
+ stmp3xxx_setl(BF(data_sample_cycles, GPMI_CTRL1_DSAMPLE_TIME),
+ REGS_GPMI_BASE + HW_GPMI_CTRL1);
+#endif
+
+ g->use_count--;
+}
+
+static inline u32 bch_mode(void)
+{
+ u32 c1 = 0;
+
+#ifdef CONFIG_MTD_NAND_GPMI_BCH
+ if (bch)
+ c1 |= BM_GPMI_CTRL1_BCH_MODE;
+#endif
+ return c1;
+}
+
+/**
+ * gpmi_nand_init_hw - initialize the hardware
+ * @pdev: pointer to platform device
+ *
+ * Initialize GPMI hardware and set default (safe) timings for NAND access.
+ * Returns error code or 0 on success
+ */
+static int gpmi_nand_init_hw(struct platform_device *pdev, int request_pins)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ struct gpmi_platform_data *gpd =
+ (struct gpmi_platform_data *)pdev->dev.platform_data;
+ int err = 0;
+
+ g->clk = clk_get(NULL, "gpmi");
+ if (IS_ERR(g->clk)) {
+ err = PTR_ERR(g->clk);
+ dev_err(&pdev->dev, "cannot set failsafe clockrate\n");
+ goto out;
+ }
+ clk_enable(g->clk);
+ if (clk <= 0)
+ clk = 24000; /* safe setting, some chips do not work on
+ speeds >= 24kHz */
+ clk_set_rate(g->clk, clk);
+
+ clk = clk_get_rate(g->clk);
+
+ if (request_pins)
+ gpd->pinmux(1);
+
+ stmp3xxx_reset_block(HW_GPMI_CTRL0 + REGS_GPMI_BASE, 1);
+
+ /* this CLEARS reset, despite of its name */
+ stmp3xxx_setl(BM_GPMI_CTRL1_DEV_RESET, REGS_GPMI_BASE + HW_GPMI_CTRL1);
+
+ /* IRQ polarity */
+ stmp3xxx_setl(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+ REGS_GPMI_BASE + HW_GPMI_CTRL1);
+
+ /* ...and ECC module */
+ stmp3xxx_setl(bch_mode(), REGS_GPMI_BASE + HW_GPMI_CTRL1);
+
+ /* choose NAND mode (1 means ATA, 0 - NAND */
+ stmp3xxx_clearl(BM_GPMI_CTRL1_GPMI_MODE,
+ REGS_GPMI_BASE + HW_GPMI_CTRL1);
+
+out:
+ return err;
+}
+
+/**
+ * gpmi_nand_release_hw - free the hardware
+ * @pdev: pointer to platform device
+ *
+ * In opposite to gpmi_nand_init_hw, release all acquired resources
+ */
+static void gpmi_nand_release_hw(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ struct gpmi_platform_data *gpd =
+ (struct gpmi_platform_data *)pdev->dev.platform_data;
+
+ stmp3xxx_setl(BM_GPMI_CTRL0_SFTRST, REGS_GPMI_BASE + HW_GPMI_CTRL0);
+
+ clk_disable(g->clk);
+ clk_put(g->clk);
+ gpd->pinmux(0);
+}
+
+static int gpmi_dma_is_error(struct gpmi_nand_data *g)
+{
+ /* u32 n = __raw_readl(g->dma_ch); */
+
+ /* CURrent DMA command */
+ u32 c = __raw_readl(REGS_APBH_BASE +
+ HW_APBH_CHn_NXTCMDAR(g->cchip->dma_ch));
+
+ if (c == g->cchip->error.handle) {
+ pr_debug("%s: dma chain has reached error terminator\n",
+ __func__);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * gpmi_dma_exchange - run DMA to exchange with NAND chip
+ *
+ * @g: structure associated with NAND chip
+ *
+ * Run DMA and wait for completion
+ */
+static int gpmi_dma_exchange(struct gpmi_nand_data *g,
+ struct stmp3xxx_dma_descriptor *d)
+{
+ struct platform_device *pdev = g->dev;
+ unsigned long timeout;
+ int err;
+
+ if (g->self_suspended)
+ gpmi_self_wakeup(g);
+ g->use_count++;
+
+ if (!g->regulator) {
+ g->regulator = regulator_get(&pdev->dev, "mmc_ssp-2");
+ if (g->regulator && !IS_ERR(g->regulator))
+ regulator_set_mode(g->regulator, REGULATOR_MODE_NORMAL);
+ else
+ g->regulator = NULL;
+ }
+
+ if (g->regulator)
+ regulator_set_current_limit(g->regulator, g->reg_uA, g->reg_uA);
+
+ init_completion(&g->done);
+ stmp3xxx_dma_enable_interrupt(g->cchip->dma_ch);
+ stmp3xxx_dma_go(g->cchip->dma_ch, d ? d : g->cchip->d, 1);
+
+ timeout = wait_for_completion_timeout(&g->done, msecs_to_jiffies(1000));
+ err = (timeout <= 0) ? -ETIMEDOUT : gpmi_dma_is_error(g);
+
+ if (err)
+ printk(KERN_ERR "%s: error %d, CS = %d, channel %d\n",
+ __func__, err, g->cchip->cs, g->cchip->dma_ch);
+
+ stmp3xxx_dma_reset_channel(g->cchip->dma_ch);
+ stmp3xxx_dma_clear_interrupt(g->cchip->dma_ch);
+
+ if (g->regulator)
+ regulator_set_current_limit(g->regulator, 0, 0);
+
+ mod_timer(&g->timer, jiffies + 4 * HZ);
+ g->use_count--;
+
+ return err;
+}
+
+/**
+ * gpmi_ecc_read_page - replacement for nand_read_page
+ *
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ */
+static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t * buf)
+{
+ struct gpmi_nand_data *g = chip->priv;
+ struct mtd_ecc_stats stats;
+ dma_addr_t bufphys, oobphys;
+ int err;
+
+ bufphys = oobphys = ~0;
+
+ if (map_buffers && virt_addr_valid(buf))
+ bufphys = dma_map_single(&g->dev->dev, buf,
+ mtd->writesize, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, bufphys))
+ bufphys = g->data_buffer_handle;
+
+ if (map_buffers)
+ oobphys = dma_map_single(&g->dev->dev, chip->oob_poi,
+ mtd->oobsize, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, oobphys))
+ oobphys = g->oob_buffer_handle;
+
+ /* ECC read */
+ (void)g->hc->read(g->hc, g->selected_chip, g->cchip->d,
+ g->cchip->error.handle, bufphys, oobphys);
+
+ err = gpmi_dma_exchange(g, NULL);
+
+ g->hc->stat(g->hc, g->selected_chip, &stats);
+
+ if (stats.failed || stats.corrected) {
+
+ pr_debug("%s: ECC failed=%d, corrected=%d\n",
+ __func__, stats.failed, stats.corrected);
+
+ g->mtd.ecc_stats.failed += stats.failed;
+ g->mtd.ecc_stats.corrected += stats.corrected;
+ }
+
+ if (!dma_mapping_error(&g->dev->dev, oobphys)) {
+ if (oobphys != g->oob_buffer_handle)
+ dma_unmap_single(&g->dev->dev, oobphys,
+ mtd->oobsize, DMA_FROM_DEVICE);
+ else {
+ memcpy(chip->oob_poi, g->oob_buffer, mtd->oobsize);
+ copies++;
+ }
+ }
+
+ if (!dma_mapping_error(&g->dev->dev, bufphys)) {
+ if (bufphys != g->data_buffer_handle)
+ dma_unmap_single(&g->dev->dev, bufphys,
+ mtd->writesize, DMA_FROM_DEVICE);
+ else {
+ memcpy(buf, g->data_buffer, mtd->writesize);
+ copies++;
+ }
+ }
+
+ /* always fill the (possible ECC bytes with FF) */
+ memset(chip->oob_poi + g->oob_free, 0xff, mtd->oobsize - g->oob_free);
+
+ return err;
+}
+
+static inline int is_ff(const u8 * buffer, size_t size)
+{
+ while (size--) {
+ if (*buffer++ != 0xff)
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * gpmi_ecc_write_page - replacement for nand_write_page
+ *
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+static void gpmi_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t * buf)
+{
+ struct gpmi_nand_data *g = chip->priv;
+ dma_addr_t bufphys, oobphys;
+ int err;
+
+ /* if we can't map it, copy it */
+ bufphys = oobphys = ~0;
+
+ if (map_buffers && virt_addr_valid(buf))
+ bufphys = dma_map_single(&g->dev->dev,
+ (void *)buf, mtd->writesize,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, bufphys)) {
+ bufphys = g->data_buffer_handle;
+ memcpy(g->data_buffer, buf, mtd->writesize);
+ copies++;
+ }
+
+ /* if OOB is all FF, leave it as such */
+ if (!is_ff(chip->oob_poi, mtd->oobsize)) {
+ if (map_buffers)
+ oobphys = dma_map_single(&g->dev->dev, chip->oob_poi,
+ mtd->oobsize, DMA_TO_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, oobphys)) {
+ oobphys = g->oob_buffer_handle;
+ memcpy(g->oob_buffer, chip->oob_poi, mtd->oobsize);
+ copies++;
+ }
+ } else
+ ff_writes++;
+
+ /* call ECC */
+ g->hc->write(g->hc, g->selected_chip, g->cchip->d,
+ g->cchip->error.handle, bufphys, oobphys);
+
+ err = gpmi_dma_exchange(g, NULL);
+ if (err < 0)
+ printk(KERN_ERR "%s: dma error\n", __func__);
+
+ if (!dma_mapping_error(&g->dev->dev, oobphys)) {
+ if (oobphys != g->oob_buffer_handle)
+ dma_unmap_single(&g->dev->dev, oobphys, mtd->oobsize,
+ DMA_TO_DEVICE);
+ }
+
+ if (bufphys != g->data_buffer_handle)
+ dma_unmap_single(&g->dev->dev, bufphys, mtd->writesize,
+ DMA_TO_DEVICE);
+}
+
+/**
+ * gpmi_write_buf - replacement for nand_write_buf
+ *
+ * @mtd: MTD device
+ * @buf: data buffer
+ * @len: length of the data buffer
+ */
+static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t * buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct stmp3xxx_dma_descriptor *chain = g->cchip->d;
+ dma_addr_t phys;
+ int err;
+
+ BUG_ON(len > mtd->writesize);
+
+ phys = ~0;
+
+ if (map_buffers && virt_addr_valid(buf))
+ phys = dma_map_single(&g->dev->dev,
+ (void *)buf, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, phys)) {
+ phys = g->write_buffer_handle;
+ memcpy(g->write_buffer, buf, len);
+ copies++;
+ }
+
+ /* write plain data */
+ chain->command->cmd =
+ BF(len, APBH_CHn_CMD_XFER_COUNT) |
+ BF(4, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBH_CHn_CMD_COMMAND__DMA_READ, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WRITE, GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF(g->selected_chip, GPMI_CTRL0_CS) |
+ BF(BV_GPMI_CTRL0_ADDRESS__NAND_DATA, GPMI_CTRL0_ADDRESS) |
+ BF(len, GPMI_CTRL0_XFER_COUNT);
+
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = phys;
+
+ err = gpmi_dma_exchange(g, NULL);
+ if (err)
+ printk(KERN_ERR "%s: dma error\n", __func__);
+
+ if (phys != g->write_buffer_handle)
+ dma_unmap_single(&g->dev->dev, phys, len, DMA_TO_DEVICE);
+
+ if (debug >= 2)
+ print_hex_dump_bytes("WBUF ", DUMP_PREFIX_OFFSET, buf, len);
+}
+
+/**
+ * gpmi_read_buf - replacement for nand_read_buf
+ *
+ * @mtd: MTD device
+ * @buf: pointer to the buffer
+ * @len: size of the buffer
+ */
+static void gpmi_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct stmp3xxx_dma_descriptor *chain;
+ dma_addr_t phys;
+ int err;
+
+ phys = ~0;
+
+ if (map_buffers && virt_addr_valid(buf))
+ phys = dma_map_single(&g->dev->dev, buf, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, phys))
+ phys = g->read_buffer_handle;
+
+ chain = g->cchip->d;
+
+ /* read data */
+ chain->command->cmd =
+ BF(len, APBH_CHn_CMD_XFER_COUNT) |
+ BF(1, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__DMA_WRITE, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__READ, GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF(g->selected_chip, GPMI_CTRL0_CS) |
+ BF(BV_GPMI_CTRL0_ADDRESS__NAND_DATA, GPMI_CTRL0_ADDRESS) |
+ BF(len, GPMI_CTRL0_XFER_COUNT);
+ chain->command->buf_ptr = phys;
+ chain++;
+
+ chain->command->cmd =
+ BF(4, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY,
+ GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF(BV_GPMI_CTRL0_ADDRESS__NAND_DATA, GPMI_CTRL0_ADDRESS) |
+ BM_GPMI_CTRL0_LOCK_CS | BF(g->selected_chip, GPMI_CTRL0_CS);
+ chain->command->pio_words[1] =
+ chain->command->pio_words[2] = chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = 0;
+
+ err = gpmi_dma_exchange(g, NULL);
+ if (err)
+ printk(KERN_ERR "%s: dma error\n", __func__);
+
+ if (phys != g->read_buffer_handle)
+ dma_unmap_single(&g->dev->dev, phys, len, DMA_FROM_DEVICE);
+ else {
+ memcpy(buf, g->read_buffer, len);
+ copies++;
+ }
+
+ if (debug >= 2)
+ print_hex_dump_bytes("RBUF ", DUMP_PREFIX_OFFSET, buf, len);
+}
+
+/**
+ * gpmi_read_byte - replacement for nand_read_byte
+ * @mtd: MTD device
+ *
+ * Uses gpmi_read_buf to read 1 byte from device
+ */
+static u8 gpmi_read_byte(struct mtd_info *mtd)
+{
+ u8 b;
+
+ gpmi_read_buf(mtd, (uint8_t *) & b, 1);
+ return b;
+}
+
+/**
+ * gpmi_read_word - replacement for nand_read_word
+ * @mtd: MTD device
+ *
+ * Uses gpmi_read_buf to read 2 bytes from device
+ */
+static u16 gpmi_read_word(struct mtd_info *mtd)
+{
+ u16 w;
+
+ gpmi_read_buf(mtd, (uint8_t *) & w, sizeof(u16));
+ return w;
+}
+
+/**
+ * gpmi_erase - erase a block and update BBT table
+ *
+ * @mtd: MTD device
+ * @instr: erase instruction
+ */
+int gpmi_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ int rc;
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct gpmi_nand_data *data = platform_get_drvdata(g->dev);
+
+ if (g->self_suspended)
+ gpmi_self_wakeup(data);
+ g->use_count++;
+
+ rc = nand_erase_nand(mtd, instr, 0);
+
+ if (rc == -EIO) /* block cannot be erased */
+ gpmi_block_mark_as(chip,
+ (instr->addr >> chip->bbt_erase_shift),
+ 0x01);
+
+ mod_timer(&g->timer, jiffies + 4 * HZ);
+ g->use_count--;
+ return rc;
+}
+
+/**
+ * gpmi_dev_ready - poll the RDY pin
+ *
+ * @mtd: MTD device
+ */
+static int gpmi_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct stmp3xxx_dma_descriptor *chain = g->cchip->d;
+ int ret;
+
+ /* wait for ready */
+ chain->command->cmd =
+ BF(4, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY,
+ GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF(BV_GPMI_CTRL0_ADDRESS__NAND_DATA,
+ GPMI_CTRL0_ADDRESS) | BF(g->selected_chip, GPMI_CTRL0_CS);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = 0;
+ chain++;
+
+ ret = gpmi_dma_exchange(g, NULL);
+ if (ret != 0)
+ printk(KERN_ERR "gpmi: gpmi_dma_exchange() timeout!\n");
+ return ret == 0;
+}
+
+/**
+ * gpmi_hwcontrol - set command/address byte to the device
+ *
+ * @mtd: MTD device
+ * @cmd: command byte
+ * @ctrl: control flags
+ */
+static void gpmi_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct stmp3xxx_dma_descriptor *chain = g->cchip->d;
+ int ret;
+
+ if ((ctrl & (NAND_ALE | NAND_CLE))) {
+ if (cmd != NAND_CMD_NONE)
+ g->cmd_buffer[g->cmd_buffer_sz++] = cmd;
+ return;
+ }
+
+ if (g->cmd_buffer_sz == 0)
+ return;
+
+ /* output command */
+ chain->command->cmd =
+ BF(g->cmd_buffer_sz, APBH_CHn_CMD_XFER_COUNT) |
+ BF(3, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__DMA_READ, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WRITE, GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF(g->selected_chip, GPMI_CTRL0_CS) |
+ BF(BV_GPMI_CTRL0_ADDRESS__NAND_CLE, GPMI_CTRL0_ADDRESS) |
+ BF(g->cmd_buffer_sz, GPMI_CTRL0_XFER_COUNT);
+ if (g->cmd_buffer_sz > 0)
+ chain->command->pio_words[0] |= BM_GPMI_CTRL0_ADDRESS_INCREMENT;
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->buf_ptr = g->cmd_buffer_handle;
+ chain++;
+
+ /* emit IRQ */
+ chain->command->cmd =
+ BF(0, APBH_CHn_CMD_CMDWORDS) |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD;
+ chain++;
+
+ /* last in chain get the irq bit set */
+ chain[-1].command->cmd |= BM_APBH_CHn_CMD_IRQONCMPLT;
+
+ if (debug >= 3)
+ print_hex_dump(KERN_INFO, "CMD ", DUMP_PREFIX_OFFSET, 16, 1,
+ g->cmd_buffer, g->cmd_buffer_sz, 1);
+
+ ret = gpmi_dma_exchange(g, NULL);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: chip %d, dma error %d on the command:\n",
+ __func__, g->selected_chip, ret);
+ print_hex_dump(KERN_INFO, "CMD ", DUMP_PREFIX_OFFSET, 16, 1,
+ g->cmd_buffer, g->cmd_buffer_sz, 1);
+ }
+
+ gpmi_dev_ready(mtd);
+
+ g->cmd_buffer_sz = 0;
+}
+
+/**
+ * gpmi_alloc_buffers - allocate DMA buffers for one chip
+ *
+ * @pdev: GPMI platform device
+ * @g: pointer to structure associated with NAND chip
+ *
+ * Allocate buffer using dma_alloc_coherent
+ */
+static int gpmi_alloc_buffers(struct platform_device *pdev,
+ struct gpmi_nand_data *g)
+{
+ g->cmd_buffer = dma_alloc_coherent(&pdev->dev,
+ g->cmd_buffer_size,
+ &g->cmd_buffer_handle, GFP_DMA);
+ if (!g->cmd_buffer)
+ goto out1;
+
+ g->write_buffer = dma_alloc_coherent(&pdev->dev,
+ g->write_buffer_size * 2,
+ &g->write_buffer_handle, GFP_DMA);
+ if (!g->write_buffer)
+ goto out2;
+
+ g->read_buffer = g->write_buffer + g->write_buffer_size;
+ g->read_buffer_handle = g->write_buffer_handle + g->write_buffer_size;
+
+ g->data_buffer = dma_alloc_coherent(&pdev->dev,
+ g->data_buffer_size,
+ &g->data_buffer_handle, GFP_DMA);
+ if (!g->data_buffer)
+ goto out3;
+
+ g->oob_buffer = dma_alloc_coherent(&pdev->dev,
+ g->oob_buffer_size,
+ &g->oob_buffer_handle, GFP_DMA);
+ if (!g->oob_buffer)
+ goto out4;
+
+ g->verify_buffer = kzalloc(2 * (g->data_buffer_size +
+ g->oob_buffer_size), GFP_KERNEL);
+ if (!g->verify_buffer)
+ goto out5;
+
+ return 0;
+
+out5:
+ dma_free_coherent(&pdev->dev, g->oob_buffer_size,
+ g->oob_buffer, g->oob_buffer_handle);
+out4:
+ dma_free_coherent(&pdev->dev, g->data_buffer_size,
+ g->data_buffer, g->data_buffer_handle);
+out3:
+ dma_free_coherent(&pdev->dev, g->write_buffer_size * 2,
+ g->write_buffer, g->write_buffer_handle);
+out2:
+ dma_free_coherent(&pdev->dev, g->cmd_buffer_size,
+ g->cmd_buffer, g->cmd_buffer_handle);
+out1:
+ return -ENOMEM;
+}
+
+/**
+ * gpmi_free_buffers - free buffers allocated by gpmi_alloc_buffers
+ *
+ * @pdev: platform device
+ * @g: pointer to structure associated with NAND chip
+ *
+ * Deallocate buffers on exit
+ */
+static void gpmi_free_buffers(struct platform_device *pdev,
+ struct gpmi_nand_data *g)
+{
+ kfree(g->verify_buffer);
+ dma_free_coherent(&pdev->dev, g->oob_buffer_size,
+ g->oob_buffer, g->oob_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->write_buffer_size * 2,
+ g->write_buffer, g->write_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->cmd_buffer_size,
+ g->cmd_buffer, g->cmd_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->data_buffer_size,
+ g->data_buffer, g->data_buffer_handle);
+}
+
+/* only used in SW-ECC or NO-ECC cases */
+static int gpmi_verify_buf(struct mtd_info *mtd, const uint8_t * buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+
+ chip->read_buf(mtd, g->verify_buffer, len);
+
+ if (memcmp(buf, g->verify_buffer, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * gpmi_ecc_read_oob - replacement for nand_read_oob
+ *
+ * @mtd: MTD device
+ * @chip: mtd->priv
+ * @page: page address
+ * @sndcmd: flag indicates that command should be sent
+ */
+int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd)
+{
+ struct gpmi_nand_data *g = chip->priv;
+ loff_t oob_offset;
+ struct mtd_ecc_stats stats;
+ dma_addr_t oobphys;
+ int ecc;
+ int ret;
+
+ ecc = g->raw_oob_mode == 0 && raw_mode == 0;
+
+ if (sndcmd) {
+ oob_offset = mtd->writesize;
+ if (likely(ecc))
+ oob_offset += chip->ecc.bytes * chip->ecc.steps;
+ chip->cmdfunc(mtd, NAND_CMD_READ0, oob_offset, page);
+ sndcmd = 0;
+ }
+
+ if (unlikely(!ecc)) {
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return 1;
+ }
+
+ oobphys = ~0;
+
+ if (map_buffers)
+ oobphys = dma_map_single(&g->dev->dev, chip->oob_poi,
+ mtd->oobsize, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, oobphys))
+ oobphys = g->oob_buffer_handle;
+
+ /* ECC read */
+ (void)g->hc->read(g->hc, g->selected_chip, g->cchip->d,
+ g->cchip->error.handle, ~0, oobphys);
+
+ ret = gpmi_dma_exchange(g, NULL);
+
+ g->hc->stat(g->hc, g->selected_chip, &stats);
+
+ if (stats.failed || stats.corrected) {
+
+ printk(KERN_DEBUG "%s: ECC failed=%d, corrected=%d\n",
+ __func__, stats.failed, stats.corrected);
+
+ g->mtd.ecc_stats.failed += stats.failed;
+ g->mtd.ecc_stats.corrected += stats.corrected;
+ }
+
+ if (oobphys != g->oob_buffer_handle)
+ dma_unmap_single(&g->dev->dev, oobphys, mtd->oobsize,
+ DMA_FROM_DEVICE);
+ else {
+ memcpy(chip->oob_poi, g->oob_buffer, mtd->oobsize);
+ copies++;
+ }
+
+ /* fill rest with ff */
+ memset(chip->oob_poi + g->oob_free, 0xff, mtd->oobsize - g->oob_free);
+
+ return ret ? ret : 1;
+}
+
+/**
+ * gpmi_ecc_write_oob - replacement for nand_write_oob
+ *
+ * @mtd: MTD device
+ * @chip: mtd->priv
+ * @page: page address
+ */
+static int gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int status = 0;
+ struct gpmi_nand_data *g = chip->priv;
+ loff_t oob_offset;
+ dma_addr_t oobphys;
+ int ecc;
+ int err = 0;
+
+ /* if OOB is all FF, leave it as such */
+ if (is_ff(chip->oob_poi, mtd->oobsize)) {
+ ff_writes++;
+
+ pr_debug("%s: Skipping an empty page 0x%x (0x%x)\n",
+ __func__, page, page << chip->page_shift);
+ return 0;
+ }
+
+ ecc = g->raw_oob_mode == 0 && raw_mode == 0;
+
+ /* Send command to start input data */
+ oob_offset = mtd->writesize;
+ if (likely(ecc)) {
+ oob_offset += chip->ecc.bytes * chip->ecc.steps;
+ memset(chip->oob_poi + g->oob_free, 0xff,
+ mtd->oobsize - g->oob_free);
+ }
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, oob_offset, page);
+
+ /* call ECC */
+ if (likely(ecc)) {
+
+ oobphys = ~0;
+
+ if (map_buffers)
+ oobphys = dma_map_single(&g->dev->dev, chip->oob_poi,
+ mtd->oobsize, DMA_TO_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, oobphys)) {
+ oobphys = g->oob_buffer_handle;
+ memcpy(g->oob_buffer, chip->oob_poi, mtd->oobsize);
+ copies++;
+ }
+
+ g->hc->write(g->hc, g->selected_chip, g->cchip->d,
+ g->cchip->error.handle, ~0, oobphys);
+
+ err = gpmi_dma_exchange(g, NULL);
+ } else
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ /* Send command to program the OOB data */
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ /* ..and wait for result */
+ status = chip->waitfunc(mtd, chip);
+
+ if (likely(ecc)) {
+ if (oobphys != g->oob_buffer_handle)
+ dma_unmap_single(&g->dev->dev, oobphys, mtd->oobsize,
+ DMA_TO_DEVICE);
+ }
+
+ if (status & NAND_STATUS_FAIL) {
+ pr_debug("%s: NAND_STATUS_FAIL\n", __func__);
+ return -EIO;
+ }
+
+ return err;
+}
+
+/**
+ * gpmi_irq - IRQ handler
+ *
+ * @irq: irq no
+ * @context: IRQ context, pointer to gpmi_nand_data
+ */
+static irqreturn_t gpmi_irq(int irq, void *context)
+{
+ struct gpmi_nand_data *g = context;
+
+ if (stmp3xxx_dma_is_interrupt(g->cchip->dma_ch)) {
+ stmp3xxx_dma_clear_interrupt(g->cchip->dma_ch);
+ complete(&g->done);
+ }
+ stmp3xxx_clearl(BM_GPMI_CTRL1_DEV_IRQ | BM_GPMI_CTRL1_TIMEOUT_IRQ,
+ REGS_GPMI_BASE + HW_GPMI_CTRL1);
+ return IRQ_HANDLED;
+}
+
+static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+
+ if (chipnr == g->selected_chip)
+ return;
+
+ g->selected_chip = chipnr;
+ g->cchip = NULL;
+
+ if (chipnr == -1)
+ return;
+
+ g->cchip = g->chips + chipnr;
+}
+
+static void gpmi_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+
+ g->saved_command(mtd, command, column, page_addr);
+}
+
+static int gpmi_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ int ret;
+
+ g->raw_oob_mode = ops->mode == MTD_OOB_RAW;
+ ret = g->saved_read_oob(mtd, from, ops);
+ g->raw_oob_mode = 0;
+ return ret;
+}
+
+static int gpmi_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ int ret;
+
+ g->raw_oob_mode = ops->mode == MTD_OOB_RAW;
+ ret = g->saved_write_oob(mtd, to, ops);
+ g->raw_oob_mode = 0;
+ return ret;
+}
+
+/**
+ * perform the needed steps between nand_scan_ident and nand_scan_tail
+ */
+static int gpmi_scan_middle(struct gpmi_nand_data *g)
+{
+ int oobsize = 0;
+
+ /* Limit to 2G size due to Kernel larger 4G space support */
+ if (g->mtd.size == 0) {
+ g->mtd.size = 1 << 31;
+ g->chip.chipsize = do_div(g->mtd.size, g->chip.numchips);
+ }
+
+ g->ecc_oob_bytes = 9;
+ switch (g->mtd.writesize) {
+ case 2048: /* 2K page */
+ g->chip.ecc.layout = &gpmi_oob_64;
+ g->chip.ecc.bytes = 9;
+ g->oob_free = 19;
+ g->hwecc_type_read = GPMI_ECC4_RD;
+ g->hwecc_type_write = GPMI_ECC4_WR;
+ oobsize = 64;
+ break;
+ case 4096:
+ g->chip.ecc.layout = &gpmi_oob_128;
+ g->chip.ecc.bytes = 18;
+ g->oob_free = 65;
+ g->hwecc_type_read = GPMI_ECC8_RD;
+ g->hwecc_type_write = GPMI_ECC8_WR;
+ oobsize = 218;
+ break;
+ default:
+ printk(KERN_ERR "Unsupported write_size %d.", g->mtd.writesize);
+ break;
+ }
+
+ g->mtd.ecclayout = g->chip.ecc.layout;
+ /* sanity check */
+ if (oobsize > NAND_MAX_OOBSIZE || g->mtd.writesize > NAND_MAX_PAGESIZE) {
+ printk(KERN_ERR "Internal error. Either page size "
+ "(%d) > max (%d) "
+ "or oob size (%d) > max(%d). Sorry.\n",
+ oobsize, NAND_MAX_OOBSIZE,
+ g->mtd.writesize, NAND_MAX_PAGESIZE);
+ return -ERANGE;
+ }
+
+ g->saved_command = g->chip.cmdfunc;
+ g->chip.cmdfunc = gpmi_command;
+
+ if (oobsize > 0) {
+ g->mtd.oobsize = oobsize;
+ /* otherwise error; oobsize should be set
+ in valid cases */
+ g->hc = gpmi_hwecc_chip_find("ecc8");
+ g->hc->setup(g->hc, 0, g->mtd.writesize, g->mtd.oobsize);
+ return 0;
+ }
+
+ return -ENXIO;
+}
+
+/**
+ * gpmi_write_page - [REPLACEABLE] write one page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @buf: the data to write
+ * @page: page number to write
+ * @cached: cached programming
+ * @raw: use _raw version of write_page
+ */
+static int gpmi_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t * buf, int page, int cached, int raw)
+{
+ struct gpmi_nand_data *g = chip->priv;
+ int status, empty_data, empty_oob;
+ int oobsz;
+#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
+ void *vbuf, *obuf;
+#if 0
+ void *voob, *ooob;
+#endif
+#endif
+
+ oobsz = likely(g->raw_oob_mode == 0 && raw_mode == 0) ?
+ g->oob_free : mtd->oobsize;
+
+ empty_data = is_ff(buf, mtd->writesize);
+ empty_oob = is_ff(buf, oobsz);
+
+ if (empty_data && empty_oob) {
+ ff_writes++;
+
+ pr_debug("%s: Skipping an empty page 0x%x (0x%x)\n",
+ __func__, page, page << chip->page_shift);
+ return 0;
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ if (likely(raw == 0))
+ chip->ecc.write_page(mtd, chip, buf);
+ else
+ chip->ecc.write_page_raw(mtd, chip, buf);
+
+ /*
+ * Cached progamming disabled for now, Not sure if its worth the
+ * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+ */
+ cached = 0;
+
+ if (!cached || !(chip->options & NAND_CACHEPRG)) {
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ status = chip->waitfunc(mtd, chip);
+
+ /*
+ * See if operation failed and additional status checks are
+ * available
+ */
+ if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+ status = chip->errstat(mtd, chip, FL_WRITING, status,
+ page);
+
+ if (status & NAND_STATUS_FAIL) {
+ pr_debug("%s: NAND_STATUS_FAIL\n", __func__);
+ return -EIO;
+ }
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+ }
+
+#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
+ if (empty_data)
+ return 0;
+
+ obuf = g->verify_buffer;
+#if 1 /* make vbuf aligned by mtd->writesize */
+ vbuf = obuf + mtd->writesize;
+#else
+ ooob = obuf + mtd->writesize;
+ vbuf = ooob + mtd->oobsize;
+ voob = vbuf + mtd->writesize;
+#endif
+
+ /* keep data around */
+ memcpy(obuf, buf, mtd->writesize);
+#if 0
+ memcpy(ooob, chip->oob_poi, oobsz);
+#endif
+ /* Send command to read back the data */
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+ if (likely(raw == 0))
+ chip->ecc.read_page(mtd, chip, vbuf);
+ else
+ chip->ecc.read_page_raw(mtd, chip, vbuf);
+
+#if 0
+ memcpy(voob, chip->oob_poi, oobsz);
+#endif
+
+ if (!empty_data && memcmp(obuf, vbuf, mtd->writesize) != 0)
+ return -EIO;
+#endif
+
+ return 0;
+}
+
+/**
+ * gpmi_read_page_raw - [Intern] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ */
+static int gpmi_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t * buf)
+{
+ chip->read_buf(mtd, buf, mtd->writesize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return 0;
+}
+
+/**
+ * gpmi_write_page_raw - [Intern] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+static void gpmi_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t * buf)
+{
+ chip->write_buf(mtd, buf, mtd->writesize);
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
+
+static int gpmi_init_chip(struct platform_device *pdev,
+ struct gpmi_nand_data *g, int n, unsigned dma_ch)
+{
+ int err;
+
+ g->chips[n].dma_ch = dma_ch;
+ g->chips[n].cs = n;
+
+ err = stmp3xxx_dma_request(dma_ch, NULL, dev_name(&pdev->dev));
+ if (err) {
+ dev_err(&pdev->dev, "can't request DMA channel 0x%x\n", dma_ch);
+ goto out_all;
+ }
+
+ err = stmp3xxx_dma_make_chain(dma_ch,
+ &g->chips[n].chain,
+ g->chips[n].d, ARRAY_SIZE(g->chips[n].d));
+ if (err) {
+ dev_err(&pdev->dev, "can't setup DMA chain\n");
+ goto out_all;
+ }
+
+ err = stmp3xxx_dma_allocate_command(dma_ch, &g->chips[n].error);
+ if (err) {
+ dev_err(&pdev->dev, "can't setup DMA chain\n");
+ goto out_all;
+ }
+
+ g->chips[n].error.command->cmd =
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+out_all:
+ return err;
+}
+
+static void gpmi_deinit_chip(struct platform_device *pdev,
+ struct gpmi_nand_data *g, int n)
+{
+ int dma_ch;
+
+ if (n < 0) {
+ for (n = 0; n < ARRAY_SIZE(g->chips); n++)
+ gpmi_deinit_chip(pdev, g, n);
+ return;
+ }
+
+ if (g->chips[n].dma_ch <= 0)
+ return;
+
+ dma_ch = g->chips[n].dma_ch;
+
+ stmp3xxx_dma_free_command(dma_ch, &g->chips[n].error);
+ stmp3xxx_dma_free_chain(&g->chips[n].chain);
+ stmp3xxx_dma_release(dma_ch);
+}
+
+#if 0
+static int gpmi_to_concat(struct mtd_partition *part, char **list)
+{
+ while (list && *list) {
+ if (strcmp(part->name, *list) == 0) {
+ pr_debug("Partition '%s' will be concatenated\n",
+ part->name);
+ return true;
+ }
+ list++;
+ }
+ pr_debug("Partition '%s' is left as-is", part->name);
+ return false;
+}
+#endif
+
+static void gpmi_create_partitions(struct gpmi_nand_data *g,
+ struct gpmi_platform_data *gpd,
+ uint64_t chipsize)
+{
+#ifdef CONFIG_MTD_PARTITIONS
+ int chip, p;
+ char chipname[20];
+
+ if (g->numchips == 1)
+ g->masters[0] = &g->mtd;
+ else {
+ for (chip = 0; chip < g->numchips; chip++) {
+ memset(g->chip_partitions + chip,
+ 0, sizeof(g->chip_partitions[chip]));
+ snprintf(chipname, sizeof(chipname),
+ "gpmi-chip-%d", chip);
+ g->chip_partitions[chip].name =
+ kstrdup(chipname, GFP_KERNEL);
+ g->chip_partitions[chip].size = chipsize;
+ g->chip_partitions[chip].offset = chipsize * chip;
+ g->chip_partitions[chip].mask_flags = 0;
+ }
+ add_mtd_partitions(&g->mtd, g->chip_partitions, g->numchips);
+ }
+ g->n_concat = 0;
+ memset(g->concat, 0, sizeof(g->concat));
+ for (chip = 0; chip < g->numchips; chip++) {
+ if (add_mtd_chip) {
+ printk(KERN_NOTICE "Adding MTD for the chip %d\n",
+ chip);
+ add_mtd_device(g->masters[chip]);
+ }
+ if (chip >= gpd->items)
+ continue;
+ add_mtd_partitions(g->masters[chip],
+ gpd->parts[chip].partitions,
+ gpd->parts[chip].nr_partitions);
+ }
+ if (g->n_concat > 0) {
+#ifdef CONFIG_MTD_CONCAT
+ if (g->n_concat == 1)
+#endif
+ for (p = 0; p < g->n_concat; p++)
+ add_mtd_device(g->concat[p]);
+#ifdef CONFIG_MTD_CONCAT
+ if (g->n_concat > 1) {
+ g->concat_mtd = mtd_concat_create(g->concat,
+ g->n_concat,
+ gpd->concat_name);
+ if (g->concat_mtd)
+ add_mtd_device(g->concat_mtd);
+ }
+#endif
+ }
+ g->custom_partitions = true;
+#endif
+}
+
+static void gpmi_delete_partitions(struct gpmi_nand_data *g)
+{
+#ifdef CONFIG_MTD_PARTITIONS
+ int chip, p;
+
+ if (!g->custom_partitions)
+ return;
+#ifdef CONFIG_MTD_CONCAT
+ if (g->concat_mtd)
+ del_mtd_device(g->concat_mtd);
+ if (g->n_concat == 1)
+#endif
+ for (p = 0; p < g->n_concat; p++)
+ del_mtd_device(g->concat[p]);
+
+ for (chip = 0; chip < g->numchips; chip++) {
+ del_mtd_partitions(g->masters[chip]);
+ if (add_mtd_chip)
+ del_mtd_device(g->masters[chip]);
+ kfree(g->chip_partitions[chip].name);
+ }
+#endif
+}
+
+/**
+ * gpmi_nand_probe - probe for the GPMI device
+ *
+ * Probe for GPMI device and discover NAND chips
+ */
+static int __init gpmi_nand_probe(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *g;
+ struct gpmi_platform_data *gpd;
+ const char *part_type = 0;
+ int err = 0;
+ struct resource *r;
+ int dma;
+ unsigned long long chipsize;
+
+ /* Allocate memory for the device structure (and zero it) */
+ g = kzalloc(sizeof(*g), GFP_KERNEL);
+ if (!g) {
+ dev_err(&pdev->dev, "failed to allocate gpmi_nand_data\n");
+ err = -ENOMEM;
+ goto out1;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get resource\n");
+ err = -ENXIO;
+ goto out2;
+ }
+ g->io_base = ioremap(r->start, r->end - r->start + 1);
+ if (!g->io_base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ err = -EIO;
+ goto out2;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ err = -EIO;
+ dev_err(&pdev->dev, "can't get IRQ resource\n");
+ goto out3;
+ }
+
+ gpd = (struct gpmi_platform_data *)pdev->dev.platform_data;
+ platform_set_drvdata(pdev, g);
+ err = gpmi_nand_init_hw(pdev, 1);
+ if (err)
+ goto out3;
+
+ init_timer(&g->timer);
+ g->timer.data = (unsigned long)g;
+ g->timer.function = gpmi_timer_expiry;
+ g->timer.expires = jiffies + 4 * HZ;
+ add_timer(&g->timer);
+ dev_dbg(&pdev->dev, "%s: timer set to %ld\n",
+ __func__, jiffies + 4 * HZ);
+
+ g->reg_uA = gpd->io_uA;
+ g->regulator = regulator_get(&pdev->dev, "mmc_ssp-2");
+ if (g->regulator && !IS_ERR(g->regulator)) {
+ regulator_set_mode(g->regulator, REGULATOR_MODE_NORMAL);
+ } else
+ g->regulator = NULL;
+
+ gpmi_set_timings(pdev, &gpmi_safe_timing);
+
+ g->irq = r->start;
+ err = request_irq(g->irq, gpmi_irq, 0, dev_name(&pdev->dev), g);
+ if (err) {
+ dev_err(&pdev->dev, "can't request GPMI IRQ\n");
+ goto out4;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get DMA resource\n");
+ goto out5;
+ }
+
+ if (r->end - r->start > GPMI_MAX_CHIPS)
+ dev_info(&pdev->dev, "too spread resource: max %d chips\n",
+ GPMI_MAX_CHIPS);
+
+ for (dma = r->start;
+ dma < min_t(int, r->end, r->start + GPMI_MAX_CHIPS); dma++) {
+ err = gpmi_init_chip(pdev, g, dma - r->start, dma);
+ if (err)
+ goto out6;
+ }
+
+ g->cmd_buffer_size = GPMI_CMD_BUF_SZ;
+ g->write_buffer_size = GPMI_WRITE_BUF_SZ;
+ g->data_buffer_size = GPMI_DATA_BUF_SZ;
+ g->oob_buffer_size = GPMI_OOB_BUF_SZ;
+
+ err = gpmi_alloc_buffers(pdev, g);
+ if (err) {
+ dev_err(&pdev->dev, "can't setup buffers\n");
+ goto out6;
+ }
+
+ g->dev = pdev;
+ g->chip.priv = g;
+ g->timing = gpmi_safe_timing;
+ g->selected_chip = -1;
+ g->ignorebad = ignorebad; /* copy global setting */
+
+ g->mtd.priv = &g->chip;
+ g->mtd.name = dev_name(&pdev->dev);
+ g->mtd.owner = THIS_MODULE;
+
+ g->chip.cmd_ctrl = gpmi_hwcontrol;
+ g->chip.read_word = gpmi_read_word;
+ g->chip.read_byte = gpmi_read_byte;
+ g->chip.read_buf = gpmi_read_buf;
+ g->chip.write_buf = gpmi_write_buf;
+ g->chip.select_chip = gpmi_select_chip;
+ g->chip.verify_buf = gpmi_verify_buf;
+ g->chip.dev_ready = gpmi_dev_ready;
+
+ g->chip.ecc.mode = NAND_ECC_HW_SYNDROME;
+ g->chip.ecc.write_oob = gpmi_ecc_write_oob;
+ g->chip.ecc.read_oob = gpmi_ecc_read_oob;
+ g->chip.ecc.write_page = gpmi_ecc_write_page;
+ g->chip.ecc.read_page = gpmi_ecc_read_page;
+ g->chip.ecc.read_page_raw = gpmi_read_page_raw;
+ g->chip.ecc.write_page_raw = gpmi_write_page_raw;
+ g->chip.ecc.size = 512;
+
+ g->chip.write_page = gpmi_write_page;
+
+ g->chip.scan_bbt = gpmi_scan_bbt;
+ g->chip.block_bad = gpmi_block_bad;
+
+ g->cmd_buffer_sz = 0;
+
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(&g->mtd, max_chips)
+ || gpmi_scan_middle(g)
+ || nand_scan_tail(&g->mtd)) {
+ dev_err(&pdev->dev, "No NAND found\n");
+ /* errors found on some step */
+ goto out7;
+ }
+
+ g->chip.options |= NAND_NO_SUBPAGE_WRITE;
+ g->chip.subpagesize = g->mtd.writesize;
+ g->mtd.subpage_sft = 0;
+
+ g->mtd.erase = gpmi_erase;
+
+ g->saved_read_oob = g->mtd.read_oob;
+ g->saved_write_oob = g->mtd.write_oob;
+ g->mtd.read_oob = gpmi_read_oob;
+ g->mtd.write_oob = gpmi_write_oob;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ if (gpd == NULL)
+ goto out_all;
+
+ if (gpd->parts[0].part_probe_types) {
+ g->nr_parts = parse_mtd_partitions(&g->mtd,
+ gpd->parts[0].
+ part_probe_types, &g->parts,
+ 0);
+ if (g->nr_parts > 0)
+ part_type = "command line";
+ else
+ g->nr_parts = 0;
+ }
+
+ if (g->nr_parts == 0 && gpd->parts[0].partitions) {
+ g->parts = gpd->parts[0].partitions;
+ g->nr_parts = gpd->parts[0].nr_partitions;
+ part_type = "static";
+ }
+
+ if (g->nr_parts == 0) {
+ dev_err(&pdev->dev, "Neither part_probe_types nor "
+ "partitions was specified in platform_data");
+ goto out_all;
+ }
+
+ dev_info(&pdev->dev, "Using %s partition definition\n", part_type);
+
+ g->numchips = g->chip.numchips;
+ chipsize = g->mtd.size;
+ do_div(chipsize, (unsigned long)g->numchips);
+
+ if (!strcmp(part_type, "command line"))
+ add_mtd_partitions(&g->mtd, g->parts, g->nr_parts);
+ else
+ gpmi_create_partitions(g, gpd, chipsize);
+
+ if (add_mtd_entire) {
+ printk(KERN_NOTICE "Adding MTD covering the whole flash\n");
+ add_mtd_device(&g->mtd);
+ }
+#else
+ add_mtd_device(&g->mtd);
+#endif
+ gpmi_uid_init("nand", &g->mtd, gpd->uid_offset, gpd->uid_size);
+
+#ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES
+ gpmi_sysfs(pdev, true);
+#endif
+ return 0;
+
+out_all:
+ ecc8_exit();
+ bch_exit();
+out7:
+ nand_release(&g->mtd);
+ gpmi_free_buffers(pdev, g);
+out6:
+ gpmi_deinit_chip(pdev, g, -1);
+out5:
+ free_irq(g->irq, g);
+out4:
+ del_timer_sync(&g->timer);
+ gpmi_nand_release_hw(pdev);
+out3:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(g->io_base);
+out2:
+ kfree(g);
+out1:
+ return err;
+}
+
+/**
+ * gpmi_nand_remove - remove a GPMI device
+ *
+ */
+static int __devexit gpmi_nand_remove(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ int i = 0;
+#ifdef CONFIG_MTD_PARTITIONS
+ struct gpmi_platform_data *gpd = pdev->dev.platform_data;
+ struct mtd_partition *platf_parts;
+#endif
+
+ gpmi_delete_partitions(g);
+ del_timer_sync(&g->timer);
+ gpmi_uid_remove("nand");
+#ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES
+ gpmi_sysfs(pdev, false);
+#endif
+ nand_release(&g->mtd);
+ gpmi_free_buffers(pdev, g);
+ gpmi_deinit_chip(pdev, g, -1);
+ gpmi_nand_release_hw(pdev);
+ free_irq(g->irq, g);
+ if (g->regulator)
+ regulator_put(g->regulator);
+
+#ifdef CONFIG_MTD_PARTITIONS
+ if (i < gpd->items && gpd->parts[i].partitions)
+ platf_parts = gpd->parts[i].partitions;
+ else
+ platf_parts = NULL;
+ if (g->parts && g->parts != platf_parts)
+ kfree(g->parts);
+#endif
+ iounmap(g->io_base);
+ kfree(g);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpmi_nand_suspend(struct platform_device *pdev, pm_message_t pm)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ int r = 0;
+
+ if (g->self_suspended)
+ gpmi_self_wakeup(g);
+ del_timer_sync(&g->timer);
+
+ r = g->mtd.suspend(&g->mtd);
+ if (r == 0)
+ gpmi_nand_release_hw(pdev);
+
+ return r;
+}
+
+static int gpmi_nand_resume(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ int r;
+
+ r = gpmi_nand_init_hw(pdev, 1);
+ gpmi_set_timings(pdev, &g->timing);
+ g->mtd.resume(&g->mtd);
+ g->timer.expires = jiffies + 4 * HZ;
+ add_timer(&g->timer);
+ return r;
+}
+#else
+#define gpmi_nand_suspend NULL
+#define gpmi_nand_resume NULL
+#endif
+
+#ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES
+static ssize_t show_timings(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct gpmi_nand_timing *ptm;
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+
+ ptm = &g->timing;
+ return sprintf(buf, "DATA_SETUP %d, DATA_HOLD %d, "
+ "ADDR_SETUP %d, DSAMPLE_TIME %d\n",
+ ptm->data_setup, ptm->data_hold,
+ ptm->address_setup, ptm->dsample_time);
+}
+
+static ssize_t store_timings(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ const char *p, *end;
+ struct gpmi_nand_timing t;
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+ char tmps[20];
+ u8 *timings[] = {
+ &t.data_setup,
+ &t.data_hold,
+ &t.address_setup,
+ &t.dsample_time,
+ NULL,
+ };
+ u8 **timing = timings;
+
+ p = buf;
+
+ /* parse values */
+ while (*timing != NULL) {
+ unsigned long t_long;
+
+ end = strchr(p, ',');
+ memset(tmps, 0, sizeof(tmps));
+ if (end)
+ strncpy(tmps, p, min_t(int, sizeof(tmps) - 1, end - p));
+ else
+ strncpy(tmps, p, sizeof(tmps) - 1);
+
+ if (strict_strtoul(tmps, 0, &t_long) < 0)
+ return -EINVAL;
+
+ if (t_long > 255)
+ return -EINVAL;
+
+ **timing = (u8) t_long;
+ timing++;
+
+ if (!end && *timing)
+ return -EINVAL;
+ p = end + 1;
+ }
+
+ gpmi_set_timings(g->dev, &t);
+
+ return size;
+}
+
+static ssize_t show_stat(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "copies\t\t%dff pages\t%d\n", copies, ff_writes);
+}
+
+static ssize_t show_chips(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+ return sprintf(buf, "%d\n", g->numchips);
+}
+
+static ssize_t show_ignorebad(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+
+ return sprintf(buf, "%d\n", g->ignorebad);
+}
+
+static ssize_t store_ignorebad(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+ const char *p = buf;
+ unsigned long v;
+
+ if (strict_strtoul(p, 0, &v) < 0)
+ return size;
+ if (v > 0)
+ v = 1;
+ if (v != g->ignorebad) {
+ if (v) {
+ g->bbt = g->chip.bbt;
+ g->chip.bbt = NULL;
+ g->ignorebad = 1;
+ } else {
+ g->chip.bbt = g->bbt;
+ g->ignorebad = 0;
+ }
+ }
+ return size;
+}
+
+static DEVICE_ATTR(timings, 0644, show_timings, store_timings);
+static DEVICE_ATTR(stat, 0444, show_stat, NULL);
+static DEVICE_ATTR(ignorebad, 0644, show_ignorebad, store_ignorebad);
+static DEVICE_ATTR(numchips, 0444, show_chips, NULL);
+
+static struct device_attribute *gpmi_attrs[] = {
+ &dev_attr_timings,
+ &dev_attr_stat,
+ &dev_attr_ignorebad,
+ &dev_attr_numchips,
+ NULL,
+};
+
+int gpmi_sysfs(struct platform_device *pdev, int create)
+{
+ int err = 0;
+ int i;
+
+ if (create) {
+ for (i = 0; gpmi_attrs[i]; i++) {
+ err = device_create_file(&pdev->dev, gpmi_attrs[i]);
+ if (err)
+ break;
+ }
+ if (err)
+ while (--i >= 0)
+ device_remove_file(&pdev->dev, gpmi_attrs[i]);
+ } else {
+ for (i = 0; gpmi_attrs[i]; i++)
+ device_remove_file(&pdev->dev, gpmi_attrs[i]);
+ }
+ return err;
+}
+#endif
+
+static struct platform_driver gpmi_nand_driver = {
+ .probe = gpmi_nand_probe,
+ .remove = __devexit_p(gpmi_nand_remove),
+ .driver = {
+ .name = "gpmi",
+ .owner = THIS_MODULE,
+ },
+ .suspend = gpmi_nand_suspend,
+ .resume = gpmi_nand_resume,
+};
+
+static LIST_HEAD(gpmi_hwecc_chips);
+
+void gpmi_hwecc_chip_add(struct gpmi_hwecc_chip *chip)
+{
+ list_add(&chip->list, &gpmi_hwecc_chips);
+}
+
+EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_add);
+
+void gpmi_hwecc_chip_remove(struct gpmi_hwecc_chip *chip)
+{
+ list_del(&chip->list);
+}
+
+EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_remove);
+
+struct gpmi_hwecc_chip *gpmi_hwecc_chip_find(char *name)
+{
+ struct gpmi_hwecc_chip *c;
+
+ list_for_each_entry(c, &gpmi_hwecc_chips, list)
+ if (strncmp(c->name, name, sizeof(c->name)) == 0)
+ return c;
+ return NULL;
+}
+
+EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_find);
+
+static int __init gpmi_nand_init(void)
+{
+ bch_init();
+ ecc8_init();
+ return platform_driver_register(&gpmi_nand_driver);
+}
+
+static void __exit gpmi_nand_exit(void)
+{
+ platform_driver_unregister(&gpmi_nand_driver);
+}
+
+module_init(gpmi_nand_init);
+module_exit(gpmi_nand_exit);
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPMI NAND driver");
+module_param(max_chips, int, 0400);
+module_param(clk, long, 0400);
+module_param(bch, int, 0600);
+module_param(map_buffers, int, 0600);
+module_param(raw_mode, int, 0600);
+module_param(debug, int, 0600);
+module_param(add_mtd_entire, int, 0400);
+module_param(add_mtd_chip, int, 0400);
+module_param(ignorebad, int, 0400);
diff --git a/drivers/mtd/nand/gpmi/gpmi-bbt.c b/drivers/mtd/nand/gpmi/gpmi-bbt.c
new file mode 100644
index 000000000000..54755f870440
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-bbt.c
@@ -0,0 +1,565 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/dma-mapping.h>
+#include <linux/ctype.h>
+#include <mach/dma.h>
+#include <mach/unique-id.h>
+#include "gpmi.h"
+
+static int boot_search_count;
+static int stride = 64;
+static int ncb_version = 3;
+module_param(boot_search_count, int, 0400);
+module_param(ncb_version, int, 0400);
+
+void *gpmi_read_page(struct mtd_info *mtd, loff_t start, void *data, int raw)
+{
+ int ret;
+ struct mtd_oob_ops ops;
+
+ if (!data)
+ data = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ if (raw)
+ ops.mode = MTD_OOB_RAW;
+ else
+ ops.mode = MTD_OOB_PLACE;
+ ops.datbuf = data;
+ ops.len = mtd->writesize;
+ ops.oobbuf = data + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ops.ooboffs = 0;
+ ret = nand_do_read_ops(mtd, start, &ops);
+
+ if (ret)
+ return NULL;
+ return data;
+}
+
+int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b)
+{
+ struct gpmi_ncb *ncb = NULL, *unencoded_ncb = NULL;
+ struct nand_chip *chip = mtd->priv;
+ int err;
+ loff_t start = 0;
+ struct mtd_oob_ops ops;
+ struct erase_info instr;
+ int ncb_count;
+
+ ncb = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!ncb) {
+ err = -ENOMEM;
+ goto out;
+ }
+ unencoded_ncb = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (!unencoded_ncb) {
+ err = -ENOMEM;
+ goto out;
+ }
+ ops.mode = -1; /* if the value is not set in switch below,
+ this will cause BUG. Take care. */
+ if (b && b->pre_ncb)
+ memcpy(unencoded_ncb, b->pre_ncb, b->pre_ncb_size);
+ else {
+ memcpy(&unencoded_ncb->fingerprint1, SIG1, sizeof(u32));
+ memcpy(&unencoded_ncb->fingerprint2, SIG_NCB, sizeof(u32));
+ if (b)
+ unencoded_ncb->timing = b->timing;
+ }
+
+ switch (ncb_version) {
+ case 0:
+ ops.mode = MTD_OOB_AUTO;
+ memcpy(ncb, unencoded_ncb, sizeof(*unencoded_ncb));
+ break;
+#ifdef CONFIG_MTD_NAND_GPMI_TA1
+ case 1:
+ ops.mode = MTD_OOB_RAW;
+ gpmi_encode_hamming_ncb_22_16(unencoded_ncb,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ ncb, mtd->writesize + mtd->oobsize);
+ break;
+#endif
+#ifdef CONFIG_MTD_NAND_GPMI_TA3
+ case 3:
+ ops.mode = MTD_OOB_RAW;
+ gpmi_encode_hamming_ncb_13_8(unencoded_ncb,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ ncb, mtd->writesize + mtd->oobsize);
+ break;
+#endif
+
+ default:
+ printk(KERN_ERR"Incorrect ncb_version = %d\n", ncb_version);
+ err = -EINVAL;
+ goto out;
+ }
+
+ ops.datbuf = (u8 *)ncb;
+ ops.len = mtd->writesize;
+ ops.oobbuf = (u8 *)ncb + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ops.ooboffs = 0;
+
+ ncb_count = 0;
+ do {
+ printk(KERN_NOTICE"GPMI: Trying to store NCB at addr %lx\n",
+ (unsigned long)start);
+ memset(&instr, 0, sizeof(instr));
+ instr.mtd = mtd;
+ instr.addr = start;
+ instr.len = (1 << chip->phys_erase_shift);
+ err = nand_erase_nand(mtd, &instr, 0);
+ if (err == 0) {
+ printk(KERN_NOTICE"GPMI: Erased, storing\n");
+ err = nand_do_write_ops(mtd, start, &ops);
+ printk(KERN_NOTICE"GPMI: NCB update %s (%d).\n",
+ err ? "failed" : "succeeded", err);
+ }
+ start += (1 << chip->phys_erase_shift);
+ ncb_count++;
+ } while (err != 0 && ncb_count < 100);
+
+ if (b)
+ b->ncbblock = start >> chip->bbt_erase_shift;
+
+out:
+ kfree(ncb);
+ kfree(unencoded_ncb);
+
+ return 0;
+}
+
+static int gpmi_redundancy_check_one(u8 *pg, int dsize, int esize, int offset,
+ int o1, int o2)
+{
+ int r;
+
+ if (o1 == o2)
+ return 0;
+
+ r = memcmp(pg + o1 * dsize, pg + o2 * dsize, dsize);
+ if (r) {
+ pr_debug("DATA copies %d and %d are different: %d\n",
+ o1, o2, r);
+ return r;
+ }
+
+ r = memcmp(pg + o1 * esize + offset,
+ pg + o2 * esize + offset, esize);
+ if (r) {
+ pr_debug("ECC copies %d and %d are different: %d\n", o1, o2, r);
+ return r;
+ }
+ pr_debug("Both DATA and ECC copies %d and %d are identical\n", o1, o2);
+ return r;
+}
+
+static int gpmi_redundancy_check(u8 *pg, int dsize, int esize, int ecc_offset)
+{
+ if (gpmi_redundancy_check_one(pg, dsize, esize, ecc_offset, 0, 1) == 0)
+ return 0;
+ if (gpmi_redundancy_check_one(pg, dsize, esize, ecc_offset, 0, 2) == 0)
+ return 0;
+ if (gpmi_redundancy_check_one(pg, dsize, esize, ecc_offset, 1, 2) == 0)
+ return 1;
+ return -1;
+}
+
+static inline int gpmi_ncb1_redundancy_check(u8 *pg)
+{
+ return gpmi_redundancy_check(pg,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES,
+ NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY);
+}
+
+static int gpmi_scan_sigmatel_bbt(
+ struct mtd_info *mtd, struct gpmi_bcb_info *nfo)
+{
+ int page, r;
+ u8 *pg;
+ struct gpmi_ncb *result = NULL;
+
+ if (boot_search_count == 0)
+ boot_search_count = 1;
+ if (nfo == NULL)
+ return -EINVAL;
+
+ pg = NULL;
+ printk(KERN_NOTICE"Scanning for NCB...\n");
+ for (page = 0; page < (1<<boot_search_count); page += stride) {
+ pg = gpmi_read_page(mtd, page * mtd->writesize,
+ pg, ncb_version != 0);
+
+ printk(KERN_NOTICE"GPMI: Checking page 0x%08X\n", page);
+
+ if (ncb_version == 0) {
+ if (memcmp(pg, SIG1, SIG_SIZE) != 0)
+ continue;
+ printk(KERN_NOTICE"GPMI: Signature found at 0x%08X\n",
+ page);
+ result = (struct gpmi_ncb *)pg;
+ }
+
+#ifdef CONFIG_MTD_NAND_GPMI_TA1
+ if (ncb_version == 1) {
+ void *dptr, *eccptr;
+
+ if (memcmp(pg, SIG1, SIG_SIZE) != 0)
+ continue;
+ printk(KERN_NOTICE"GPMI: Signature found at 0x%08X\n",
+ page);
+
+ r = gpmi_ncb1_redundancy_check(pg);
+
+ if (r < 0) {
+ printk(KERN_ERR"GPMI: Oops. All three "
+ "copies of NCB are differrent!\n");
+ continue;
+ }
+
+ dptr = pg + r * NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES;
+ eccptr = pg + NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY +
+ r * NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES;
+
+ if (gpmi_verify_hamming_22_16(dptr, eccptr,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES) < 0) {
+ printk(KERN_ERR"Verification failed.\n");
+ continue;
+ }
+ result = (struct gpmi_ncb *)pg;
+ }
+#endif
+
+#ifdef CONFIG_MTD_NAND_GPMI_TA3
+ if (ncb_version == 3) {
+
+ if (memcmp(pg + 12, SIG1, SIG_SIZE) != 0)
+ continue;
+
+ printk(KERN_NOTICE"GPMI: Signature found at 0x%08X\n",
+ page);
+
+ if (gpmi_verify_hamming_13_8(
+ pg + 12,
+ pg + 524,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES) < 0) {
+ printk(KERN_ERR"Verification failed.\n");
+ continue;
+ }
+ result = (struct gpmi_ncb *)(pg + 12);
+ }
+#endif
+ if (result) {
+ printk(KERN_NOTICE"GPMI: Valid NCB found "
+ "at 0x%08x\n", page);
+ nfo->timing = result->timing;
+ nfo->ncbblock = page * mtd->writesize;
+ break;
+ }
+ }
+ kfree(pg);
+
+ return result != NULL;
+}
+
+int gpmi_scan_bbt(struct mtd_info *mtd)
+{
+ struct gpmi_bcb_info stmp_bbt;
+ struct nand_chip *this = mtd->priv;
+ struct gpmi_nand_data *g = this->priv;
+ int r;
+ int numblocks, from, i, ign;
+
+ memset(&stmp_bbt, 0, sizeof(stmp_bbt));
+ g->transcribe_bbmark = 0;
+
+ /*
+ Since NCB uses the full page, including BB pattern bits,
+ driver has to ignore result of gpmi_block_bad when reading
+ these pages.
+ */
+ ign = g->ignorebad;
+
+ g->ignorebad = true; /* strictly speaking, I'd have to hide
+ * the BBT too.
+ * But we still scanning it :) */
+ r = gpmi_scan_sigmatel_bbt(mtd, &stmp_bbt);
+
+ /* and then, driver has to restore the setting */
+ g->ignorebad = ign;
+
+ if (r) {
+ printk(KERN_NOTICE"Setting discovered timings: %d:%d:%d:%d\n",
+ stmp_bbt.timing.data_setup,
+ stmp_bbt.timing.data_hold,
+ stmp_bbt.timing.address_setup,
+ stmp_bbt.timing.dsample_time);
+
+ gpmi_set_timings(g->dev, &stmp_bbt.timing);
+ g->timing = stmp_bbt.timing;
+
+ } else {
+ g->transcribe_bbmark = !0;
+ numblocks = this->chipsize >> this->bbt_erase_shift;
+ from = 0;
+ printk(KERN_NOTICE"Checking BB on common-formatted flash\n");
+ for (i = stmp_bbt.ncbblock + 1; i < numblocks; i++) {
+ /* check the block and transcribe the bb if needed */
+ gpmi_block_bad(mtd, from, 0);
+ from += (1 << this->bbt_erase_shift);
+ }
+ }
+
+ r = nand_default_bbt(mtd);
+
+ if (g->transcribe_bbmark) {
+ /* NCB has been not found, so create NCB now */
+ g->transcribe_bbmark = 0;
+
+ stmp_bbt.timing = gpmi_safe_timing;
+ r = gpmi_write_ncb(mtd, &stmp_bbt);
+ } else {
+ /* NCB found, and its block should be marked as "good" */
+ gpmi_block_mark_as(this, stmp_bbt.ncbblock, 0x00);
+ }
+
+ return r;
+}
+
+int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+
+{
+ int page, res = 0;
+ struct nand_chip *chip = mtd->priv;
+ u16 bad;
+ struct gpmi_nand_data *g = chip->priv;
+ int chipnr;
+
+ /* badblockpos is an offset in OOB area */
+ int badblockpos = chip->ecc.steps * chip->ecc.bytes;
+
+ if (g->ignorebad)
+ return 0;
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
+ chipnr = (int)(ofs >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ if (g->transcribe_bbmark)
+ /* bad block marks still are on first byte of OOB */
+ badblockpos = 0;
+
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, badblockpos & 0xFE,
+ page);
+ bad = cpu_to_le16(chip->read_word(mtd));
+ if (badblockpos & 0x1)
+ bad >>= 8;
+ if ((bad & 0xFF) != 0xff)
+ res = 1;
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, badblockpos, page);
+ if (chip->read_byte(mtd) != 0xff)
+ res = 1;
+ }
+
+ if (g->transcribe_bbmark && res)
+ chip->block_markbad(mtd, ofs);
+
+ chip->select_chip(mtd, -1);
+
+ return res;
+}
+
+#if defined(CONFIG_STMP3XXX_UNIQUE_ID)
+/*
+ * UID on NAND support
+ */
+const int uid_size = 256;
+
+struct gpmi_uid_context {
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ u_int32_t start;
+ u_int32_t size;
+};
+
+static int gpmi_read_uid(struct gpmi_uid_context *ctx, void *result)
+{
+ void *pg = NULL;
+ int page, o;
+ int status = -ENOENT;
+ int h_size = gpmi_hamming_ecc_size_22_16(uid_size);
+
+ for (page = ctx->start >> ctx->nand->page_shift;
+ page < (ctx->start + ctx->size) >> ctx->nand->page_shift;) {
+ pr_debug("%s: reading page 0x%x\n", __func__, page);
+ if (gpmi_block_bad(ctx->mtd, page * ctx->mtd->writesize, 0)) {
+ pr_debug("%s: bad block %x, skipping it\n",
+ __func__, page * ctx->mtd->writesize);
+ page += (1 << ctx->nand->phys_erase_shift)
+ >> ctx->nand->page_shift;
+ continue;
+ }
+ pg = gpmi_read_page(ctx->mtd, page * ctx->mtd->writesize,
+ pg, 0);
+ if (pg)
+ break;
+ page++;
+ }
+
+ if (!pg)
+ return status;
+
+ o = gpmi_redundancy_check(pg, uid_size, h_size, 3 * uid_size);
+ if (o >= 0) {
+ if (gpmi_verify_hamming_22_16(
+ pg + o * uid_size,
+ pg + 3 * uid_size + h_size, uid_size) >= 0) {
+ memcpy(result, pg + o * uid_size, uid_size);
+ status = 0;
+ }
+ }
+ kfree(pg);
+ return status;
+}
+
+static int gpmi_write_uid(struct gpmi_uid_context *ctx, void *src)
+{
+ struct mtd_oob_ops ops;
+ struct erase_info instr;
+ u8 *data = kzalloc(ctx->mtd->writesize + ctx->mtd->oobsize, GFP_KERNEL);
+ int h_size = gpmi_hamming_ecc_size_22_16(uid_size);
+ char ecc[h_size];
+ u_int32_t start;
+ int i;
+ int err;
+
+ if (!data)
+ return -ENOMEM;
+
+ gpmi_encode_hamming_22_16(src, uid_size, ecc, h_size);
+ for (i = 0; i < 3; i++) {
+ memcpy(data + i * uid_size, src, uid_size);
+ memcpy(data + 3 * uid_size + i * h_size, ecc, h_size);
+ }
+
+ ops.mode = MTD_OOB_AUTO;
+ ops.datbuf = data;
+ ops.len = ctx->mtd->writesize;
+ ops.oobbuf = NULL;
+ ops.ooblen = ctx->mtd->oobsize;
+ ops.ooboffs = 0;
+
+ start = ctx->start;
+
+ do {
+ memset(&instr, 0, sizeof(instr));
+ instr.mtd = ctx->mtd;
+ instr.addr = start;
+ instr.len = (1 << ctx->nand->phys_erase_shift);
+ err = nand_erase_nand(ctx->mtd, &instr, 0);
+ if (err == 0)
+ err = nand_do_write_ops(ctx->mtd, start, &ops);
+ start += (1 << ctx->nand->phys_erase_shift);
+ if (start > ctx->start + ctx->size)
+ break;
+ } while (err != 0);
+
+ return err;
+}
+
+static ssize_t gpmi_uid_store(void *context, const char *page,
+ size_t count, int ascii)
+{
+ u8 data[uid_size];
+
+ memset(data, 0, sizeof(data));
+ memcpy(data, page, uid_size < count ? uid_size : count);
+ gpmi_write_uid(context, data);
+ return count;
+}
+
+static ssize_t gpmi_uid_show(void *context, char *page, int ascii)
+{
+ u8 result[uid_size];
+ int i;
+ char *p = page;
+ int r;
+
+ r = gpmi_read_uid(context, result);
+ if (r < 0)
+ return r;
+
+ if (ascii) {
+ for (i = 0; i < uid_size; i++) {
+ if (i % 16 == 0) {
+ if (i)
+ *p++ = '\n';
+ sprintf(p, "%04X: ", i);
+ p += strlen(p);
+ }
+ sprintf(p, "%02X ", result[i]);
+ p += strlen(p);
+ }
+ *p++ = '\n';
+ return p - page;
+
+ } else {
+ memcpy(page, result, uid_size);
+ return uid_size;
+ }
+}
+
+static struct uid_ops gpmi_uid_ops = {
+ .id_show = gpmi_uid_show,
+ .id_store = gpmi_uid_store,
+};
+
+static struct gpmi_uid_context gpmi_uid_context;
+
+int __init gpmi_uid_init(const char *name, struct mtd_info *mtd,
+ u_int32_t start, u_int32_t size)
+{
+ gpmi_uid_context.mtd = mtd;
+ gpmi_uid_context.nand = mtd->priv;
+ gpmi_uid_context.start = start;
+ gpmi_uid_context.size = size;
+ return uid_provider_init(name, &gpmi_uid_ops, &gpmi_uid_context) ?
+ 0 : -EFAULT;
+}
+
+void gpmi_uid_remove(const char *name)
+{
+ uid_provider_remove(name);
+}
+#endif
diff --git a/drivers/mtd/nand/gpmi/gpmi-bch.c b/drivers/mtd/nand/gpmi/gpmi-bch.c
new file mode 100644
index 000000000000..d7b261778dcf
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-bch.c
@@ -0,0 +1,293 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * STMP378X BCH hardware ECC engine
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/dma.h>
+#include <mach/stmp3xxx.h>
+#include <mach/platform.h>
+#include <mach/irqs.h>
+#include <mach/regs-gpmi.h>
+#include "gpmi.h"
+
+#define BCH_MAX_NANDS 4
+
+static int bch_available(void *context);
+static int bch_setup(void *context, int index, int writesize, int oobsize);
+static int bch_read(void *context,
+ int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+static int bch_stat(void *ctx, int index, struct mtd_ecc_stats *r);
+static int bch_reset(void *context, int index);
+
+struct bch_state_t {
+ struct gpmi_hwecc_chip chip;
+ struct {
+ struct mtd_ecc_stats stat;
+ struct completion done;
+ u32 writesize, oobsize;
+ } nands[BCH_MAX_NANDS];
+};
+
+static struct bch_state_t state = {
+ .chip = {
+ .name = "bch",
+ .setup = bch_setup,
+ .stat = bch_stat,
+ .read = bch_read,
+ .reset = bch_reset,
+ },
+};
+
+static int bch_reset(void *context, int index)
+{
+ stmp3xxx_reset_block(REGS_BCH_BASE, true);
+ stmp3xxx_setl(BM_BCH_CTRL_COMPLETE_IRQ_EN, REGS_BCH_BASE + HW_BCH_CTRL);
+ return 0;
+}
+
+static int bch_stat(void *context, int index, struct mtd_ecc_stats *r)
+{
+ struct bch_state_t *state = context;
+
+ *r = state->nands[index].stat;
+ state->nands[index].stat.failed = 0;
+ state->nands[index].stat.corrected = 0;
+ return 0;
+}
+
+static irqreturn_t bch_irq(int irq, void *context)
+{
+ u32 b0, s0;
+ struct mtd_ecc_stats stat;
+ int r;
+ struct bch_state_t *state = context;
+
+ s0 = __raw_readl(REGS_BCH_BASE + HW_BCH_STATUS0);
+ r = (s0 & BM_BCH_STATUS0_COMPLETED_CE) >> 16;
+
+ stat.corrected = stat.failed = 0;
+
+ b0 = (s0 & BM_BCH_STATUS0_STATUS_BLK0) >> 8;
+ if (b0 <= 4)
+ stat.corrected += b0;
+ if (b0 == 0xFE)
+ stat.failed++;
+
+ if (s0 & BM_BCH_STATUS0_CORRECTED)
+ stat.corrected += (s0 & BM_BCH_STATUS0_CORRECTED);
+ if (s0 & BM_BCH_STATUS0_UNCORRECTABLE)
+ stat.failed++;
+
+ stmp3xxx_clearl(BM_BCH_CTRL_COMPLETE_IRQ, REGS_BCH_BASE + HW_BCH_CTRL);
+
+ pr_debug("%s: chip %d, failed %d, corrected %d\n",
+ __func__, r,
+ state->nands[r].stat.failed,
+ state->nands[r].stat.corrected);
+ state->nands[r].stat.corrected += stat.corrected;
+ state->nands[r].stat.failed += stat.failed;
+ complete(&state->nands[r].done);
+
+ return IRQ_HANDLED;
+}
+
+static int bch_available(void *context)
+{
+ stmp3xxx_reset_block(REGS_BCH_BASE, 0);
+ return __raw_readl(REGS_BCH_BASE + HW_BCH_BLOCKNAME) == 0x20484342;
+}
+
+static int bch_setup(void *context, int index, int writesize, int oobsize)
+{
+ struct bch_state_t *state = context;
+ u32 layout = (u32)REGS_BCH_BASE + 0x80 + index * 0x20;
+ u32 ecc0, eccN;
+ u32 reg;
+ int meta;
+
+ switch (writesize) {
+ case 2048:
+ ecc0 = 4;
+ eccN = 4;
+ meta = 5;
+ break;
+ case 4096:
+ ecc0 = 16;
+ eccN = 14;
+ meta = 10;
+ break;
+ default:
+ printk(KERN_ERR"%s: cannot tune BCH for page size %d\n",
+ __func__, writesize);
+ return -EINVAL;
+ }
+
+ state->nands[index].oobsize = oobsize;
+ state->nands[index].writesize = writesize;
+
+ __raw_writel(BF(writesize/512, BCH_FLASH0LAYOUT0_NBLOCKS) |
+ BF(oobsize, BCH_FLASH0LAYOUT0_META_SIZE) |
+ BF(ecc0 >> 1, BCH_FLASH0LAYOUT0_ECC0) | /* for oob */
+ BF(0x00, BCH_FLASH0LAYOUT0_DATA0_SIZE), layout);
+ __raw_writel(BF(writesize + oobsize, BCH_FLASH0LAYOUT1_PAGE_SIZE) |
+ BF(eccN >> 1, BCH_FLASH0LAYOUT1_ECCN) | /* for dblock */
+ BF(512, BCH_FLASH0LAYOUT1_DATAN_SIZE), layout + 0x10);
+
+ /*
+ * since driver only supports CS 0..3, layouts are mapped 1:1 :
+ * FLASHnLAYOUT[1,2] => LAYOUTSELECT[n*2:n2*+1]
+ */
+ reg = __raw_readl(REGS_BCH_BASE + HW_BCH_LAYOUTSELECT);
+ reg &= ~(0x03 << (index * 2));
+ reg |= index << (index * 2);
+ __raw_writel(reg, REGS_BCH_BASE + HW_BCH_LAYOUTSELECT);
+
+ bch_reset(context, index);
+
+ printk(KERN_DEBUG"%s: CS = %d, LAYOUT = 0x%08X, layout_reg = "
+ "0x%08x+0x%08x: 0x%08x+0x%08x\n",
+ __func__,
+ index, __raw_readl(REGS_BCH_BASE + HW_BCH_LAYOUTSELECT),
+ layout, layout + 0x10,
+ __raw_readl(layout),
+ __raw_readl(layout+0x10));
+ return 0;
+}
+
+static int bch_read(void *context,
+ int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob)
+{
+ unsigned long readsize = 0;
+ u32 bufmask = 0;
+ struct bch_state_t *state = context;
+
+ if (!dma_mapping_error(NULL, oob)) {
+ bufmask |= BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+ readsize += state->nands[index].oobsize;
+ }
+ if (!dma_mapping_error(NULL, page)) {
+ bufmask |= (BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE
+ & ~BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY);
+ readsize += state->nands[index].writesize;
+ }
+
+ printk(KERN_DEBUG"readsize = %ld, bufmask = 0x%X\n", readsize, bufmask);
+ bch_reset(context, index);
+
+ /* wait for ready */
+ chain->command->cmd =
+ BF(1, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY,
+ GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF(BV_GPMI_CTRL0_ADDRESS__NAND_DATA, GPMI_CTRL0_ADDRESS) |
+ BF(index, GPMI_CTRL0_CS);
+ chain->command->alternate = 0;
+ chain++;
+
+ /* enable BCH and read NAND data */
+ chain->command->cmd =
+ BF(6, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__READ, GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF(index, GPMI_CTRL0_CS) |
+ BF(readsize, GPMI_CTRL0_XFER_COUNT);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ BF(0x02, GPMI_ECCCTRL_ECC_CMD) |
+ BF(bufmask, GPMI_ECCCTRL_BUFFER_MASK);
+ chain->command->pio_words[3] = readsize;
+ chain->command->pio_words[4] = !dma_mapping_error(NULL, page) ? page : 0;
+ chain->command->pio_words[5] = !dma_mapping_error(NULL, oob) ? oob : 0;
+ chain->command->alternate = 0;
+ chain++;
+
+ /* disable BCH block */
+ chain->command->cmd =
+ BF(3, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY,
+ GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF(index, GPMI_CTRL0_CS) |
+ BF(readsize, GPMI_CTRL0_XFER_COUNT);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->alternate = 0;
+ chain++;
+
+ /* and deassert nand lock */
+ chain->command->cmd =
+ BF(0, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->alternate = 0;
+
+ return 0;
+}
+
+int __init bch_init(void)
+{
+ int err;
+
+ if (!bch_available(&state.chip))
+ return -ENXIO;
+ gpmi_hwecc_chip_add(&state.chip);
+ err = request_irq(IRQ_BCH, bch_irq, 0, state.chip.name, &state);
+ if (err)
+ return err;
+
+ printk(KERN_DEBUG"%s: initialized\n", __func__);
+ return 0;
+}
+
+void bch_exit(void)
+{
+ free_irq(IRQ_BCH, &state);
+ gpmi_hwecc_chip_remove(&state.chip);
+}
diff --git a/drivers/mtd/nand/gpmi/gpmi-ecc8.c b/drivers/mtd/nand/gpmi/gpmi-ecc8.c
new file mode 100644
index 000000000000..ead809cf0d54
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-ecc8.c
@@ -0,0 +1,387 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * STMP37XX/STMP378X ECC8 hardware ECC engine
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/dma.h>
+#include <mach/stmp3xxx.h>
+#include <mach/irqs.h>
+#include <mach/regs-gpmi.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-apbh.h>
+#include <mach/platform.h>
+#include "gpmi.h"
+
+#define ECC8_MAX_NANDS 4
+
+static int ecc8_available(void *context);
+static int ecc8_setup(void *context, int index, int writesize, int oobsize);
+static int ecc8_read(void *context, int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+static int ecc8_write(void *context, int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+static int ecc8_stat(void *ctx, int index, struct mtd_ecc_stats *r);
+static int ecc8_reset(void *context, int index);
+
+struct ecc8_nand {
+};
+
+struct ecc8_state_t {
+ struct gpmi_hwecc_chip chip;
+ struct completion done;
+ struct mtd_ecc_stats stat;
+ u32 writesize, oobsize;
+ u32 ecc_page, ecc_oob, oob_free;
+ u32 r, w;
+ int bits;
+ u32 s0_mask, s1_mask;
+};
+
+static struct ecc8_state_t state = {
+ .chip = {
+ .name = "ecc8",
+ .setup = ecc8_setup,
+ .stat = ecc8_stat,
+ .read = ecc8_read,
+ .write = ecc8_write,
+ .reset = ecc8_reset,
+ },
+};
+
+static int ecc8_reset(void *context, int index)
+{
+ stmp3xxx_reset_block(REGS_ECC8_BASE, false);
+ while (__raw_readl(REGS_ECC8_BASE + HW_ECC8_CTRL) & BM_ECC8_CTRL_AHBM_SFTRST)
+ stmp3xxx_clearl(BM_ECC8_CTRL_AHBM_SFTRST, REGS_ECC8_BASE + HW_ECC8_CTRL);
+ stmp3xxx_setl(BM_ECC8_CTRL_COMPLETE_IRQ_EN, REGS_ECC8_BASE + HW_ECC8_CTRL);
+ return 0;
+}
+
+static int ecc8_stat(void *context, int index, struct mtd_ecc_stats *r)
+{
+ struct ecc8_state_t *state = context;
+
+ wait_for_completion(&state->done);
+
+ *r = state->stat;
+ state->stat.failed = 0;
+ state->stat.corrected = 0;
+ return 0;
+}
+
+static irqreturn_t ecc8_irq(int irq, void *context)
+{
+ int r;
+ struct mtd_ecc_stats ecc_stats;
+ struct ecc8_state_t *state = context;
+ u32 corr;
+ u32 s0 = __raw_readl(REGS_ECC8_BASE + HW_ECC8_STATUS0),
+ s1 = __raw_readl(REGS_ECC8_BASE + HW_ECC8_STATUS1);
+
+ r = (s0 & BM_ECC8_STATUS0_COMPLETED_CE) >> 16;
+
+ if (s0 & BM_ECC8_STATUS0_CORRECTED ||
+ s0 & BM_ECC8_STATUS0_UNCORRECTABLE) {
+
+ ecc_stats.failed = 0;
+ ecc_stats.corrected = 0;
+
+ s0 = (s0 & BM_ECC8_STATUS0_STATUS_AUX) >> 8;
+ if (s0 <= 4)
+ ecc_stats.corrected += s0;
+ if (s0 == 0xE)
+ ecc_stats.failed++;
+
+ for ( ; s1 != 0; s1 >>= 4) {
+ corr = s1 & 0xF;
+ if (corr == 0x0C)
+ continue;
+ if (corr == 0xE)
+ ecc_stats.failed++;
+ if (corr <= 8)
+ ecc_stats.corrected += corr;
+ s1 >>= 4;
+ }
+ state->stat.corrected += ecc_stats.corrected;
+ state->stat.failed += ecc_stats.failed;
+ }
+
+ complete(&state->done);
+
+ stmp3xxx_clearl(BM_ECC8_CTRL_COMPLETE_IRQ, REGS_ECC8_BASE + HW_ECC8_CTRL);
+ return IRQ_HANDLED;
+}
+
+static int ecc8_available(void *context)
+{
+ return 1;
+}
+
+static int ecc8_setup(void *context, int index, int writesize, int oobsize)
+{
+ struct ecc8_state_t *state = context;
+
+ switch (writesize) {
+ case 2048:
+ state->ecc_page = 9;
+ state->ecc_oob = 9;
+ state->bits = 4;
+ state->r = BF(BV_GPMI_ECCCTRL_ECC_CMD__DECODE_4_BIT,
+ GPMI_ECCCTRL_ECC_CMD);
+ state->w = BF(BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT,
+ GPMI_ECCCTRL_ECC_CMD);
+ break;
+ case 4096:
+ state->ecc_page = 18;
+ state->ecc_oob = 9;
+ state->bits = 8;
+ state->r = BF(BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT,
+ GPMI_ECCCTRL_ECC_CMD);
+ state->w = BF(BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT,
+ GPMI_ECCCTRL_ECC_CMD);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ state->oob_free = oobsize -
+ (state->bits * state->ecc_page) - state->ecc_oob;
+ state->oobsize = oobsize;
+ state->writesize = writesize;
+
+ ecc8_reset(context, index);
+
+ return 0;
+}
+
+/**
+ * ecc8_read - create dma chain to read nand page
+ *
+ * @context: context data, pointer to ecc8_state
+ * @index: CS of nand to read
+ * @chain: dma chain to be filled
+ * @page: (physical) address of page data to be read to
+ * @oob: (physical) address of OOB data to be read to
+ *
+ * Return: status of operation -- 0 on success
+ */
+static int ecc8_read(void *context, int cs,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob)
+{
+ unsigned long readsize = 0;
+ u32 bufmask = 0;
+ struct ecc8_state_t *state = context;
+
+ ecc8_reset(context, cs);
+
+ state->s0_mask = state->s1_mask = 0;
+
+ if (!dma_mapping_error(NULL, page)) {
+ bufmask |= (1 << state->bits) - 1;
+ readsize += (state->bits * state->ecc_page);
+ readsize += state->writesize;
+ }
+ if (!dma_mapping_error(NULL, oob)) {
+ bufmask |= BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY;
+ readsize += state->oob_free + state->ecc_oob;
+ state->s0_mask = BM_ECC8_STATUS0_STATUS_AUX;
+ if (dma_mapping_error(NULL, page))
+ page = oob;
+ }
+
+
+ /* wait for ready */
+ chain->command->cmd =
+ BF(1, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY,
+ GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF(BV_GPMI_CTRL0_ADDRESS__NAND_DATA, GPMI_CTRL0_ADDRESS) |
+ BF(cs, GPMI_CTRL0_CS);
+ chain->command->buf_ptr = 0;
+ chain++;
+
+ /* enable ECC and read NAND data */
+ chain->command->cmd =
+ BF(6, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__READ, GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF(cs, GPMI_CTRL0_CS) |
+ BF(readsize, GPMI_CTRL0_XFER_COUNT);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ state->r |
+ BF(bufmask, GPMI_ECCCTRL_BUFFER_MASK);
+ chain->command->pio_words[3] = readsize;
+ chain->command->pio_words[4] = !dma_mapping_error(NULL, page) ? page : 0;
+ chain->command->pio_words[5] = !dma_mapping_error(NULL, oob) ? oob : 0;
+ chain->command->buf_ptr = 0;
+ chain++;
+
+ /* disable ECC block */
+ chain->command->cmd =
+ BF(3, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY,
+ GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF(cs, GPMI_CTRL0_CS);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->buf_ptr = 0;
+ chain++;
+
+ /* and deassert nand lock */
+ chain->command->cmd =
+ BF(0, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER, APBH_CHn_CMD_COMMAND);
+ chain->command->buf_ptr = 0;
+
+ init_completion(&state->done);
+
+ return 0;
+}
+
+static int ecc8_write(void *context, int cs,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob)
+{
+ u32 bufmask = 0;
+ struct ecc8_state_t *state = context;
+ u32 data_w, data_w_ecc,
+ oob_w, oob_w_ecc;
+
+ ecc8_reset(context, cs);
+
+ data_w = data_w_ecc = oob_w = oob_w_ecc = 0;
+
+ if (!dma_mapping_error(NULL, oob)) {
+ bufmask |= BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY;
+ oob_w = state->oob_free;
+ oob_w_ecc = oob_w + state->ecc_oob;
+ }
+ if (!dma_mapping_error(NULL, page)) {
+ bufmask |= (1 << state->bits) - 1;
+ data_w = state->bits * 512;
+ data_w_ecc = data_w + state->bits * state->ecc_page;
+ }
+
+ /* enable ECC and send NAND data (page only) */
+ chain->command->cmd =
+ BF(data_w ? data_w : oob_w, APBH_CHn_CMD_XFER_COUNT) |
+ BF(4, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(BV_APBH_CHn_CMD_COMMAND__DMA_READ, APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ BF(BV_GPMI_CTRL0_COMMAND_MODE__WRITE, GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF(cs, GPMI_CTRL0_CS) |
+ BF(BV_GPMI_CTRL0_ADDRESS__NAND_DATA, GPMI_CTRL0_ADDRESS) |
+ BF(data_w + oob_w, GPMI_CTRL0_XFER_COUNT);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ state->w |
+ BF(bufmask, GPMI_ECCCTRL_BUFFER_MASK);
+ chain->command->pio_words[3] =
+ data_w_ecc + oob_w_ecc;
+ if (!dma_mapping_error(NULL, page))
+ chain->command->buf_ptr = page;
+ else
+ chain->command->buf_ptr = oob;
+ chain++;
+
+ if (!dma_mapping_error(NULL, page) && !dma_mapping_error(NULL, oob)) {
+ /* send NAND data (OOB only) */
+ chain->command->cmd =
+ BM_APBH_CHn_CMD_CHAIN |
+ BF(oob_w, APBH_CHn_CMD_XFER_COUNT) |
+ BF(0, APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BF(BV_APBH_CHn_CMD_COMMAND__DMA_READ,
+ APBH_CHn_CMD_COMMAND);
+ chain->command->buf_ptr = oob; /* never dma_mapping_error() */
+ chain++;
+ }
+ /* emit IRQ */
+ chain->command->cmd =
+ BF(0, APBH_CHn_CMD_CMDWORDS) |
+ BF(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER,
+ APBH_CHn_CMD_COMMAND) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_IRQONCMPLT;
+
+ return 0;
+}
+
+int __init ecc8_init(void)
+{
+ int err;
+
+ if (!ecc8_available(&state))
+ return -ENXIO;
+
+ gpmi_hwecc_chip_add(&state.chip);
+ err = request_irq(IRQ_ECC8_IRQ, ecc8_irq, 0, state.chip.name, &state);
+ if (err)
+ return err;
+
+ printk(KERN_INFO"%s: initialized\n", __func__);
+ return 0;
+}
+
+void ecc8_exit(void)
+{
+ free_irq(IRQ_ECC8_IRQ, &state);
+ gpmi_hwecc_chip_remove(&state.chip);
+}
diff --git a/drivers/mtd/nand/gpmi/gpmi-hamming-13-8.c b/drivers/mtd/nand/gpmi/gpmi-hamming-13-8.c
new file mode 100644
index 000000000000..256911050782
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-hamming-13-8.c
@@ -0,0 +1,130 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * NCB software ECC Hamming code
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+
+#define BIT_VAL(v, n) (((v) >> (n)) & 0x1)
+#define B(n) (BIT_VAL(d, n))
+
+static u8 calculate_parity(u8 d)
+{
+ u8 p = 0;
+
+ if (d == 0 || d == 0xFF)
+ return 0; /* optimization :) */
+
+ p |= (B(6) ^ B(5) ^ B(3) ^ B(2)) << 0;
+ p |= (B(7) ^ B(5) ^ B(4) ^ B(2) ^ B(1)) << 1;
+ p |= (B(7) ^ B(6) ^ B(5) ^ B(1) ^ B(0)) << 2;
+ p |= (B(7) ^ B(4) ^ B(3) ^ B(0)) << 3;
+ p |= (B(6) ^ B(4) ^ B(3) ^ B(2) ^ B(1) ^ B(0)) << 4;
+ return p;
+}
+
+static inline int even_number_of_1s(u8 byte)
+{
+ int even = 1;
+
+ while (byte > 0) {
+ even ^= (byte & 0x1);
+ byte >>= 1;
+ }
+ return even;
+}
+
+static int lookup_single_error(u8 syndrome)
+{
+ int i;
+ u8 syndrome_table[] = {
+ 0x1C, 0x16, 0x13, 0x19,
+ 0x1A, 0x07, 0x15, 0x0E,
+ 0x01, 0x02, 0x04, 0x08,
+ 0x10,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(syndrome_table); i++)
+ if (syndrome_table[i] == syndrome)
+ return i;
+ return -ENOENT;
+}
+
+int gpmi_verify_hamming_13_8(void *data, u8 *parity, size_t size)
+{
+ int i;
+ u8 *pdata = data;
+ int bit_to_flip;
+ u8 np, syndrome;
+ int errors = 0;
+
+ for (i = 0; i < size; i ++, pdata++) {
+ np = calculate_parity(*pdata);
+ syndrome = np ^ parity[i];
+ if (syndrome == 0) /* cool */ {
+ continue;
+ }
+
+ if (even_number_of_1s(syndrome))
+ return -i; /* can't recover */
+
+ bit_to_flip = lookup_single_error(syndrome);
+ if (bit_to_flip < 0)
+ return -i; /* can't fix the error */
+
+ if (bit_to_flip < 8) {
+ *pdata ^= (1 << bit_to_flip);
+ errors++;
+ }
+ }
+ return errors;
+}
+
+void gpmi_encode_hamming_13_8(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size)
+{
+ int i;
+ u8 *src = source_block;
+ u8 *ecc = source_ecc;
+
+ for (i = 0; i < src_size && i < ecc_size; i++)
+ ecc[i] = calculate_parity(src[i]);
+}
+
+void gpmi_encode_hamming_ncb_13_8(void *source_block, size_t source_size,
+ void *target_block, size_t target_size)
+{
+ if (target_size < 12 + 2 * NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
+ return;
+ memset(target_block, 0xFF, target_size);
+ memcpy((u8 *)target_block + 12, source_block, source_size);
+ gpmi_encode_hamming_13_8(source_block,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ (u8 *)target_block + 12 + NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+}
+
+unsigned gpmi_hamming_ecc_size_13_8(int block_size)
+{
+ return block_size;
+}
diff --git a/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c
new file mode 100644
index 000000000000..cceb35feab2e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c
@@ -0,0 +1,195 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * NCB software ECC Hamming code
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+#include "gpmi-hamming-22-16.h"
+
+#define BIT_VAL(v, n) (((v) >> (n)) & 0x1)
+#define B(n) (BIT_VAL(d, n))
+#define BSEQ(a1, a2, a3, a4, a5, a6, a7, a8) \
+ (B(a1) ^ B(a2) ^ B(a3) ^ B(a4) ^ B(a5) ^ B(a6) ^ B(a7) ^ B(a8))
+static u8 calculate_parity(u16 d)
+{
+ u8 p = 0;
+
+ if (d == 0 || d == 0xFFFF)
+ return 0; /* optimization :) */
+
+ p |= BSEQ(15, 12, 11, 8, 5, 4, 3, 2) << 0;
+ p |= BSEQ(13, 12, 11, 10, 9, 7, 3, 1) << 1;
+ p |= BSEQ(15, 14, 13, 11, 10, 9, 6, 5) << 2;
+ p |= BSEQ(15, 14, 13, 8, 7, 6, 4, 0) << 3;
+ p |= BSEQ(12, 9, 8, 7, 6, 2, 1, 0) << 4;
+ p |= BSEQ(14, 10, 5, 4, 3, 2, 1, 0) << 5;
+ return p;
+}
+
+static inline int even_number_of_1s(u8 byte)
+{
+ int even = 1;
+
+ while (byte > 0) {
+ even ^= (byte & 0x1);
+ byte >>= 1;
+ }
+ return even;
+}
+
+static int lookup_single_error(u8 syndrome)
+{
+ int i;
+ u8 syndrome_table[] = {
+ 0x38, 0x32, 0x31, 0x23, 0x29, 0x25, 0x1C, 0x1A,
+ 0x19, 0x16, 0x26, 0x07, 0x13, 0x0E, 0x2C, 0x0D,
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(syndrome_table); i++)
+ if (syndrome_table[i] == syndrome)
+ return i;
+ return -ENOENT;
+}
+
+int gpmi_verify_hamming_22_16(void *data, u8 *parity, size_t size)
+{
+ int i, j;
+ int bit_index, bit_to_flip;
+ u16 *pdata = data;
+ u8 p = 0, np, syndrome;
+ int errors = 0;
+
+ for (bit_index = i = j = 0;
+ i < size / sizeof(u16);
+ i ++, pdata++) {
+
+ switch (bit_index) {
+
+ case 0:
+ p = parity[j] & 0x3F;
+ break;
+ case 2:
+ p = (parity[j++] & 0xC0) >> 6;
+ p |= (parity[j] & 0x0F) << 2;
+ break;
+ case 4:
+ p = (parity[j++] & 0xF0) >> 4;
+ p |= (parity[j] & 0x03) << 4;
+ break;
+ case 6:
+ p = (parity[j++] & 0xFC) >> 2;
+ break;
+ default:
+ BUG(); /* how did you get this ?! */
+ break;
+ }
+ bit_index = (bit_index + 2) % 8;
+
+ np = calculate_parity(*pdata);
+ syndrome = np ^ p;
+ if (syndrome == 0) /* cool */ {
+ continue;
+ }
+
+ if (even_number_of_1s(syndrome))
+ return -i; /* can't recover */
+
+ bit_to_flip = lookup_single_error(syndrome);
+ if (bit_to_flip < 0)
+ return -i; /* can't fix the error */
+
+ if (bit_to_flip < 16) {
+ *pdata ^= (1 << bit_to_flip);
+ errors++;
+ }
+ }
+ return errors;
+}
+
+void gpmi_encode_hamming_22_16(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size)
+{
+ int i, j, bit_index;
+ u16 *src = source_block;
+ u8 *ecc = source_ecc;
+ u8 np;
+
+ for (bit_index = j = i = 0;
+ j < src_size/sizeof(u16) && i < ecc_size;
+ j++) {
+
+ np = calculate_parity(src[j]);
+
+ switch (bit_index) {
+
+ case 0:
+ ecc[i] = np & 0x3F;
+ break;
+ case 2:
+ ecc[i++] |= (np & 0x03) << 6;
+ ecc[i] = (np & 0x3C) >> 2;
+ break;
+ case 4:
+ ecc[i++] |= (np & 0x0F) << 4;
+ ecc[i] = (np & 0x30) >> 4;
+ break;
+ case 6:
+ ecc[i++] |= (np & 0x3F) << 2;
+ break;
+ }
+ bit_index = (bit_index + 2) % 8;
+ }
+
+}
+
+void gpmi_encode_hamming_ncb_22_16(void *source_block, size_t source_size,
+ void *target_block, size_t target_size)
+{
+ u8 *dst = target_block;
+ u8 ecc[NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES];
+
+ gpmi_encode_hamming_22_16(source_block,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ ecc,
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ /* create THREE copies of source block */
+ memcpy(dst + NAND_HC_ECC_OFFSET_FIRST_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_SECOND_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_THIRD_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ /* ..and three copies of ECC block */
+ memcpy(dst + NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_THIRD_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+}
+
+unsigned gpmi_hamming_ecc_size_22_16(int block_size)
+{
+ return (((block_size * 8) / 16) * 6) / 8;
+}
diff --git a/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.h b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.h
new file mode 100644
index 000000000000..6959bf3c599e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.h
@@ -0,0 +1,43 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * NCB software ECC Hamming code
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __LINUX_GPMI_H
+#define __LINUX_GPMI_H
+
+#define NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES (512)
+#define NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES \
+ ((((NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES*8)/16)*6)/8)
+#define NAND_HC_ECC_OFFSET_FIRST_DATA_COPY (0)
+#define NAND_HC_ECC_OFFSET_SECOND_DATA_COPY \
+ (NAND_HC_ECC_OFFSET_FIRST_DATA_COPY + \
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
+#define NAND_HC_ECC_OFFSET_THIRD_DATA_COPY \
+ (NAND_HC_ECC_OFFSET_SECOND_DATA_COPY + \
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
+#define NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY \
+ (NAND_HC_ECC_OFFSET_THIRD_DATA_COPY + \
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
+#define NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY \
+ (NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY + \
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES)
+#define NAND_HC_ECC_OFFSET_THIRD_PARITY_COPY \
+ (NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY + \
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES)
+
+#endif
diff --git a/drivers/mtd/nand/gpmi/gpmi.h b/drivers/mtd/nand/gpmi/gpmi.h
new file mode 100644
index 000000000000..65de2e1a1c78
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi.h
@@ -0,0 +1,293 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __DRIVERS_GPMI_H
+#define __DRIVERS_GPMI_H
+
+#include <linux/mtd/partitions.h>
+#include <linux/timer.h>
+#include <mach/gpmi.h>
+#include <mach/regs-gpmi.h>
+#include <mach/regs-apbh.h>
+#include <mach/dma.h>
+#ifdef CONFIG_MTD_NAND_GPMI_BCH
+#include <mach/regs-bch.h>
+#endif
+#include <mach/regs-ecc8.h>
+
+#include "gpmi-hamming-22-16.h"
+
+#define GPMI_ECC4_WR \
+ (BM_GPMI_ECCCTRL_ENABLE_ECC | \
+ BF(BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT, GPMI_ECCCTRL_ECC_CMD))
+#define GPMI_ECC4_RD \
+ (BM_GPMI_ECCCTRL_ENABLE_ECC | \
+ BF(BV_GPMI_ECCCTRL_ECC_CMD__DECODE_4_BIT, GPMI_ECCCTRL_ECC_CMD))
+#define GPMI_ECC8_WR \
+ (BM_GPMI_ECCCTRL_ENABLE_ECC | \
+ BF(BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT, GPMI_ECCCTRL_ECC_CMD))
+#define GPMI_ECC8_RD \
+ (BM_GPMI_ECCCTRL_ENABLE_ECC | \
+ BF(BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT, GPMI_ECCCTRL_ECC_CMD))
+
+/* fingerprints of BCB that can be found on STMP-formatted flash */
+#define SIG1 "STMP"
+#define SIG_NCB "NCB "
+#define SIG_LDLB "LDLB"
+#define SIG_DBBT "DBBT"
+#define SIG_SIZE 4
+
+struct gpmi_nand_timing {
+ u8 data_setup;
+ u8 data_hold;
+ u8 address_setup;
+ u8 dsample_time;
+};
+
+struct gpmi_bcb_info {
+ struct gpmi_nand_timing timing;
+ loff_t ncbblock;
+ const void *pre_ncb;
+ size_t pre_ncb_size;
+};
+
+struct gpmi_ncb;
+
+int gpmi_erase(struct mtd_info *mtd, struct erase_info *instr);
+int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs);
+int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip);
+int gpmi_scan_bbt(struct mtd_info *mtd);
+int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip);
+#ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES
+int gpmi_sysfs(struct platform_device *p, int create);
+#endif
+int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd);
+void gpmi_set_timings(struct platform_device *pdev,
+ struct gpmi_nand_timing *tm);
+int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b);
+
+unsigned gpmi_hamming_ecc_size_22_16(int block_size);
+void gpmi_encode_hamming_ncb_22_16(void *source_block, size_t source_size,
+ void *target_block, size_t target_size);
+void gpmi_encode_hamming_22_16(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size);
+int gpmi_verify_hamming_22_16(void *data, u8 *parity, size_t size);
+
+unsigned gpmi_hamming_ecc_size_13_8(int block_size);
+void gpmi_encode_hamming_ncb_13_8(void *source_block, size_t source_size,
+ void *target_block, size_t target_size);
+void gpmi_encode_hamming_13_8(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size);
+int gpmi_verify_hamming_13_8(void *data, u8 *parity, size_t size);
+
+#define GPMI_DMA_MAX_CHAIN 20 /* max DMA commands in chain */
+
+/*
+ * Sizes of data buffers to exchange commands/data with NAND chip
+ * Default values cover 4K NAND page (4096 data bytes + 218 bytes OOB)
+ */
+#define GPMI_CMD_BUF_SZ 10
+#define GPMI_DATA_BUF_SZ NAND_MAX_PAGESIZE
+#define GPMI_WRITE_BUF_SZ NAND_MAX_PAGESIZE
+#define GPMI_OOB_BUF_SZ NAND_MAX_OOBSIZE
+
+#define GPMI_MAX_CHIPS 10
+
+struct gpmi_hwecc_chip {
+ char name[40];
+ struct list_head list;
+ int (*setup)(void *ctx, int index, int writesize, int oobsize);
+ int (*reset)(void *ctx, int index);
+ int (*read)(void *ctx, int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+ int (*write)(void *ctx, int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+ int (*stat)(void *ctx, int index, struct mtd_ecc_stats *r);
+};
+
+/* HWECC chips */
+struct gpmi_hwecc_chip *gpmi_hwecc_chip_find(char *name);
+void gpmi_hwecc_chip_add(struct gpmi_hwecc_chip *chip);
+void gpmi_hwecc_chip_remove(struct gpmi_hwecc_chip *chip);
+int bch_init(void);
+int ecc8_init(void);
+void bch_exit(void);
+void ecc8_exit(void);
+
+
+struct gpmi_nand_data {
+ void __iomem *io_base;
+ struct clk *clk;
+ int irq;
+ struct timer_list timer;
+ int self_suspended;
+ int use_count;
+ struct regulator *regulator;
+ int reg_uA;
+
+ int ignorebad;
+ void *bbt;
+
+ struct nand_chip chip;
+ struct mtd_info mtd;
+ struct platform_device *dev;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ int nr_parts;
+ struct mtd_partition
+ *parts;
+#endif
+
+ struct completion done;
+
+ u8 *cmd_buffer;
+ dma_addr_t cmd_buffer_handle;
+ int cmd_buffer_size, cmd_buffer_sz;
+
+ u8 *write_buffer;
+ dma_addr_t write_buffer_handle;
+ int write_buffer_size;
+ u8 *read_buffer; /* point in write_buffer */
+ dma_addr_t read_buffer_handle;
+
+ u8 *data_buffer;
+ dma_addr_t data_buffer_handle;
+ int data_buffer_size;
+
+ u8 *oob_buffer;
+ dma_addr_t oob_buffer_handle;
+ int oob_buffer_size;
+
+ void *verify_buffer;
+
+ struct nchip {
+ unsigned dma_ch;
+ struct stmp37xx_circ_dma_chain chain;
+ struct stmp3xxx_dma_descriptor d[GPMI_DMA_MAX_CHAIN];
+ struct stmp3xxx_dma_descriptor error;
+ int cs;
+ } chips[GPMI_MAX_CHIPS];
+ struct nchip *cchip;
+ int selected_chip;
+
+ unsigned hwecc_type_read, hwecc_type_write;
+ int hwecc;
+
+ int ecc_oob_bytes, oob_free;
+
+ int transcribe_bbmark;
+ struct gpmi_nand_timing timing;
+
+ void (*saved_command)(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr);
+
+ int raw_oob_mode;
+ int (*saved_read_oob)(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops);
+ int (*saved_write_oob)(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
+
+ struct gpmi_hwecc_chip *hc;
+
+ int numchips;
+ int custom_partitions;
+ struct mtd_info *masters[GPMI_MAX_CHIPS];
+ struct mtd_partition chip_partitions[GPMI_MAX_CHIPS];
+ int n_concat;
+ struct mtd_info *concat[GPMI_MAX_CHIPS];
+ struct mtd_info *concat_mtd;
+};
+
+extern struct gpmi_nand_timing gpmi_safe_timing;
+
+struct gpmi_ncb {
+ u32 fingerprint1;
+ struct gpmi_nand_timing timing;
+ u32 pagesize;
+ u32 page_plus_oob_size;
+ u32 sectors_per_block;
+ u32 sector_in_page_mask;
+ u32 sector_to_page_shift;
+ u32 num_nands;
+ u32 reserved[3];
+ u32 fingerprint2; /* offset 0x2C */
+};
+
+struct gpmi_ldlb {
+ u32 fingerprint1;
+ u16 major, minor, sub, reserved;
+ u32 nand_bitmap;
+ u32 reserved1[7];
+ u32 fingerprint2;
+ struct {
+ u32 fw_starting_nand;
+ u32 fw_starting_sector;
+ u32 fw_sector_stride;
+ u32 fw_sectors_total;
+ } fw[2];
+ u16 fw_major, fw_minor, fw_sub, fw_reserved;
+ u32 bbt_blk;
+ u32 bbt_blk_backup;
+};
+
+static inline void gpmi_block_mark_as(struct nand_chip *chip,
+ int block, int mark)
+{
+ u32 o;
+ int shift = (block & 0x03) << 1,
+ index = block >> 2;
+
+ if (chip->bbt) {
+ mark &= 0x03;
+
+ o = chip->bbt[index];
+ o &= ~(0x03 << shift);
+ o |= (mark << shift);
+ chip->bbt[index] = o;
+ }
+}
+
+static inline int gpmi_block_badness(struct nand_chip *chip,
+ int block)
+{
+ u32 o;
+ int shift = (block & 0x03) << 1,
+ index = block >> 2;
+
+ if (chip->bbt) {
+ o = (chip->bbt[index] >> shift) & 0x03;
+ pr_debug("%s: block = %d, o = %d\n", __func__, block, o);
+ return o;
+ }
+ return -1;
+}
+
+#ifdef CONFIG_STMP3XXX_UNIQUE_ID
+int __init gpmi_uid_init(const char *name, struct mtd_info *mtd,
+ u_int32_t start, u_int32_t size);
+void gpmi_uid_remove(const char *name);
+#else
+#define gpmi_uid_init(name, mtd, start, size)
+#define gpmi_uid_remove(name)
+#endif
+
+#endif
diff --git a/drivers/mtd/nand/imx_nfc.c b/drivers/mtd/nand/imx_nfc.c
new file mode 100644
index 000000000000..65f72b4e468a
--- /dev/null
+++ b/drivers/mtd/nand/imx_nfc.c
@@ -0,0 +1,8286 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+#define DRIVER_VERSION "1.0"
+
+/* Define this macro to enable event reporting. */
+
+#define EVENT_REPORTING
+
+/*
+ * For detailed information that will be helpful in understanding this driver,
+ * see:
+ *
+ * Documentation/imx_nfc.txt
+ */
+
+/*
+ * Macros that describe NFC hardware have names of the form:
+ *
+ * NFC_*
+ *
+ * Macros that apply only to specific versions of the NFC have names of the
+ * following form:
+ *
+ * NFC_<M>_<N>_*
+ *
+ * where:
+ *
+ * <M> is the major version of the NFC hardware.
+ * <N> is the minor version of the NFC hardware.
+ *
+ * The minor version can be 'X', which means that the macro applies to *all*
+ * NFCs of the same major version.
+ *
+ * For NFC versions with only one set of registers, macros that give offsets
+ * against the base address have names of the form:
+ *
+ * *<RegisterName>_REG_OFF
+ *
+ * Macros that give the position of a field's LSB within a given register have
+ * names of the form:
+ *
+ * *<RegisterName>_<FieldName>_POS
+ *
+ * Macros that mask a field within a given register have names of the form:
+ *
+ * *<RegisterName>_<FieldName>_MSK
+ */
+
+/*
+ * Macro definitions for ALL NFC versions.
+ */
+
+#define NFC_MAIN_BUF_SIZE (512)
+
+/*
+ * Macro definitions for version 1.0 NFCs.
+ */
+
+#define NFC_1_0_BUF_SIZE_REG_OFF (0x00)
+#define NFC_1_0_BUF_ADDR_REG_OFF (0x04)
+#define NFC_1_0_FLASH_ADDR_REG_OFF (0x06)
+#define NFC_1_0_FLASH_CMD_REG_OFF (0x08)
+#define NFC_1_0_CONFIG_REG_OFF (0x0A)
+#define NFC_1_0_ECC_STATUS_RESULT_REG_OFF (0x0C)
+#define NFC_1_0_RSLTMAIN_AREA_REG_OFF (0x0E)
+#define NFC_1_0_RSLTSPARE_AREA_REG_OFF (0x10)
+#define NFC_1_0_WRPROT_REG_OFF (0x12)
+#define NFC_1_0_UNLOCKSTART_BLKADDR_REG_OFF (0x14)
+#define NFC_1_0_UNLOCKEND_BLKADDR_REG_OFF (0x16)
+#define NFC_1_0_NF_WRPRST_REG_OFF (0x18)
+
+#define NFC_1_0_CONFIG1_REG_OFF (0x1A)
+#define NFC_1_0_CONFIG1_NF_CE_POS (7)
+#define NFC_1_0_CONFIG1_NF_CE_MSK (0x1 << 7)
+
+#define NFC_1_0_CONFIG2_REG_OFF (0x1C)
+
+/*
+* Macro definitions for version 2.X NFCs.
+*/
+
+#define NFC_2_X_FLASH_ADDR_REG_OFF (0x06)
+#define NFC_2_X_FLASH_CMD_REG_OFF (0x08)
+#define NFC_2_X_CONFIG_REG_OFF (0x0A)
+
+#define NFC_2_X_WR_PROT_REG_OFF (0x12)
+
+#define NFC_2_X_NF_WR_PR_ST_REG_OFF (0x18)
+
+#define NFC_2_X_CONFIG2_REG_OFF (0x1C)
+#define NFC_2_X_CONFIG2_FCMD_POS (0)
+#define NFC_2_X_CONFIG2_FCMD_MSK (0x1 << 0)
+#define NFC_2_X_CONFIG2_FADD_POS (1)
+#define NFC_2_X_CONFIG2_FADD_MSK (0x1 << 1)
+#define NFC_2_X_CONFIG2_FDI_POS (2)
+#define NFC_2_X_CONFIG2_FDI_MSK (0x1 << 2)
+#define NFC_2_X_CONFIG2_FDO_POS (3)
+#define NFC_2_X_CONFIG2_FDO_MSK (0x7 << 3)
+#define NFC_2_X_CONFIG2_INT_POS (15)
+#define NFC_2_X_CONFIG2_INT_MSK (0x1 << 15)
+
+/*
+* Macro definitions for version 2.0 NFCs.
+*/
+
+#define NFC_2_0_BUF_ADDR_REG_OFF (0x04)
+#define NFC_2_0_BUF_ADDR_RBA_POS (0)
+#define NFC_2_0_BUF_ADDR_RBA_MSK (0xf << 0)
+
+#define NFC_2_0_ECC_STATUS_REG_OFF (0x0C)
+#define NFC_2_0_ECC_STATUS_NOSER1_POS (0)
+#define NFC_2_0_ECC_STATUS_NOSER1_MSK (0xF << 0)
+#define NFC_2_0_ECC_STATUS_NOSER2_POS (4)
+#define NFC_2_0_ECC_STATUS_NOSER2_MSK (0xF << 4)
+#define NFC_2_0_ECC_STATUS_NOSER3_POS (8)
+#define NFC_2_0_ECC_STATUS_NOSER3_MSK (0xF << 8)
+#define NFC_2_0_ECC_STATUS_NOSER4_POS (12)
+#define NFC_2_0_ECC_STATUS_NOSER4_MSK (0xF << 12)
+
+#define NFC_2_0_CONFIG1_REG_OFF (0x1A)
+#define NFC_2_0_CONFIG1_SP_EN_POS (2)
+#define NFC_2_0_CONFIG1_SP_EN_MSK (0x1 << 2)
+#define NFC_2_0_CONFIG1_ECC_EN_POS (3)
+#define NFC_2_0_CONFIG1_ECC_EN_MSK (0x1 << 3)
+#define NFC_2_0_CONFIG1_INT_MSK_POS (4)
+#define NFC_2_0_CONFIG1_INT_MSK_MSK (0x1 << 4)
+#define NFC_2_0_CONFIG1_NF_BIG_POS (5)
+#define NFC_2_0_CONFIG1_NF_BIG_MSK (0x1 << 5)
+#define NFC_2_0_CONFIG1_NFC_RST_POS (6)
+#define NFC_2_0_CONFIG1_NFC_RST_MSK (0x1 << 6)
+#define NFC_2_0_CONFIG1_NF_CE_POS (7)
+#define NFC_2_0_CONFIG1_NF_CE_MSK (0x1 << 7)
+#define NFC_2_0_CONFIG1_ONE_CYLE_POS (8)
+#define NFC_2_0_CONFIG1_ONE_CYLE_MSK (0x1 << 8)
+#define NFC_2_0_CONFIG1_MLC_POS (9)
+#define NFC_2_0_CONFIG1_MLC_MSK (0x1 << 9)
+
+#define NFC_2_0_UNLOCK_START_REG_OFF (0x14)
+#define NFC_2_0_UNLOCK_END_REG_OFF (0x16)
+
+/*
+* Macro definitions for version 2.1 NFCs.
+*/
+
+#define NFC_2_1_BUF_ADDR_REG_OFF (0x04)
+#define NFC_2_1_BUF_ADDR_RBA_POS (0)
+#define NFC_2_1_BUF_ADDR_RBA_MSK (0x7 << 0)
+#define NFC_2_1_BUF_ADDR_CS_POS (4)
+#define NFC_2_1_BUF_ADDR_CS_MSK (0x3 << 4)
+
+#define NFC_2_1_ECC_STATUS_REG_OFF (0x0C)
+#define NFC_2_1_ECC_STATUS_NOSER1_POS (0)
+#define NFC_2_1_ECC_STATUS_NOSER1_MSK (0xF << 0)
+#define NFC_2_1_ECC_STATUS_NOSER2_POS (4)
+#define NFC_2_1_ECC_STATUS_NOSER2_MSK (0xF << 4)
+#define NFC_2_1_ECC_STATUS_NOSER3_POS (8)
+#define NFC_2_1_ECC_STATUS_NOSER3_MSK (0xF << 8)
+#define NFC_2_1_ECC_STATUS_NOSER4_POS (12)
+#define NFC_2_1_ECC_STATUS_NOSER4_MSK (0xF << 12)
+
+#define NFC_2_1_CONFIG1_REG_OFF (0x1A)
+#define NFC_2_1_CONFIG1_ECC_MODE_POS (0)
+#define NFC_2_1_CONFIG1_ECC_MODE_MSK (0x1 << 0)
+#define NFC_2_1_CONFIG1_DMA_MODE_POS (1)
+#define NFC_2_1_CONFIG1_DMA_MODE_MSK (0x1 << 1)
+#define NFC_2_1_CONFIG1_SP_EN_POS (2)
+#define NFC_2_1_CONFIG1_SP_EN_MSK (0x1 << 2)
+#define NFC_2_1_CONFIG1_ECC_EN_POS (3)
+#define NFC_2_1_CONFIG1_ECC_EN_MSK (0x1 << 3)
+#define NFC_2_1_CONFIG1_INT_MSK_POS (4)
+#define NFC_2_1_CONFIG1_INT_MSK_MSK (0x1 << 4)
+#define NFC_2_1_CONFIG1_NF_BIG_POS (5)
+#define NFC_2_1_CONFIG1_NF_BIG_MSK (0x1 << 5)
+#define NFC_2_1_CONFIG1_NFC_RST_POS (6)
+#define NFC_2_1_CONFIG1_NFC_RST_MSK (0x1 << 6)
+#define NFC_2_1_CONFIG1_NF_CE_POS (7)
+#define NFC_2_1_CONFIG1_NF_CE_MSK (0x1 << 7)
+#define NFC_2_1_CONFIG1_SYM_POS (8)
+#define NFC_2_1_CONFIG1_SYM_MSK (0x1 << 8)
+#define NFC_2_1_CONFIG1_PPB_POS (9)
+#define NFC_2_1_CONFIG1_PPB_MSK (0x3 << 9)
+#define NFC_2_1_CONFIG1_FP_INT_POS (11)
+#define NFC_2_1_CONFIG1_FP_INT_MSK (0x1 << 11)
+
+#define NFC_2_1_UNLOCK_START_0_REG_OFF (0x20)
+#define NFC_2_1_UNLOCK_END_0_REG_OFF (0x22)
+#define NFC_2_1_UNLOCK_START_1_REG_OFF (0x24)
+#define NFC_2_1_UNLOCK_END_1_REG_OFF (0x26)
+#define NFC_2_1_UNLOCK_START_2_REG_OFF (0x28)
+#define NFC_2_1_UNLOCK_END_2_REG_OFF (0x2A)
+#define NFC_2_1_UNLOCK_START_3_REG_OFF (0x2C)
+#define NFC_2_1_UNLOCK_END_3_REG_OFF (0x2E)
+
+/*
+* Macro definitions for version 3.X NFCs.
+*/
+
+/*
+* Macro definitions for version 3.1 NFCs.
+*/
+
+#define NFC_3_1_FLASH_ADDR_CMD_REG_OFF (0x00)
+#define NFC_3_1_CONFIG1_REG_OFF (0x04)
+#define NFC_3_1_ECC_STATUS_RESULT_REG_OFF (0x08)
+#define NFC_3_1_LAUNCH_NFC_REG_OFF (0x0C)
+
+#define NFC_3_1_WRPROT_REG_OFF (0x00)
+#define NFC_3_1_WRPROT_UNLOCK_BLK_ADD0_REG_OFF (0x04)
+#define NFC_3_1_CONFIG2_REG_OFF (0x14)
+#define NFC_3_1_IPC_REG_OFF (0x18)
+
+/*
+* Macro definitions for version 3.2 NFCs.
+*/
+
+#define NFC_3_2_CMD_REG_OFF (0x00)
+
+#define NFC_3_2_ADD0_REG_OFF (0x04)
+#define NFC_3_2_ADD1_REG_OFF (0x08)
+#define NFC_3_2_ADD2_REG_OFF (0x0C)
+#define NFC_3_2_ADD3_REG_OFF (0x10)
+#define NFC_3_2_ADD4_REG_OFF (0x14)
+#define NFC_3_2_ADD5_REG_OFF (0x18)
+#define NFC_3_2_ADD6_REG_OFF (0x1C)
+#define NFC_3_2_ADD7_REG_OFF (0x20)
+#define NFC_3_2_ADD8_REG_OFF (0x24)
+#define NFC_3_2_ADD9_REG_OFF (0x28)
+#define NFC_3_2_ADD10_REG_OFF (0x2C)
+#define NFC_3_2_ADD11_REG_OFF (0x30)
+
+#define NFC_3_2_CONFIG1_REG_OFF (0x34)
+#define NFC_3_2_CONFIG1_SP_EN_POS (0)
+#define NFC_3_2_CONFIG1_SP_EN_MSK (0x1 << 0)
+#define NFC_3_2_CONFIG1_NF_CE_POS (1)
+#define NFC_3_2_CONFIG1_NF_CE_MSK (0x1 << 1)
+#define NFC_3_2_CONFIG1_RST_POS (2)
+#define NFC_3_2_CONFIG1_RST_MSK (0x1 << 2)
+#define NFC_3_2_CONFIG1_RBA_POS (4)
+#define NFC_3_2_CONFIG1_RBA_MSK (0x7 << 4)
+#define NFC_3_2_CONFIG1_ITER_POS (8)
+#define NFC_3_2_CONFIG1_ITER_MSK (0xf << 8)
+#define NFC_3_2_CONFIG1_CS_POS (12)
+#define NFC_3_2_CONFIG1_CS_MSK (0x7 << 12)
+#define NFC_3_2_CONFIG1_STATUS_POS (16)
+#define NFC_3_2_CONFIG1_STATUS_MSK (0xFFFF<<16)
+
+#define NFC_3_2_ECC_STATUS_REG_OFF (0x38)
+#define NFC_3_2_ECC_STATUS_NOBER1_POS (0)
+#define NFC_3_2_ECC_STATUS_NOBER1_MSK (0xF << 0)
+#define NFC_3_2_ECC_STATUS_NOBER2_POS (4)
+#define NFC_3_2_ECC_STATUS_NOBER2_MSK (0xF << 4)
+#define NFC_3_2_ECC_STATUS_NOBER3_POS (8)
+#define NFC_3_2_ECC_STATUS_NOBER3_MSK (0xF << 8)
+#define NFC_3_2_ECC_STATUS_NOBER4_POS (12)
+#define NFC_3_2_ECC_STATUS_NOBER4_MSK (0xF << 12)
+#define NFC_3_2_ECC_STATUS_NOBER5_POS (16)
+#define NFC_3_2_ECC_STATUS_NOBER5_MSK (0xF << 16)
+#define NFC_3_2_ECC_STATUS_NOBER6_POS (20)
+#define NFC_3_2_ECC_STATUS_NOBER6_MSK (0xF << 20)
+#define NFC_3_2_ECC_STATUS_NOBER7_POS (24)
+#define NFC_3_2_ECC_STATUS_NOBER7_MSK (0xF << 24)
+#define NFC_3_2_ECC_STATUS_NOBER8_POS (28)
+#define NFC_3_2_ECC_STATUS_NOBER8_MSK (0xF << 28)
+
+
+#define NFC_3_2_STATUS_SUM_REG_OFF (0x3C)
+#define NFC_3_2_STATUS_SUM_NAND_SUM_POS (0x0)
+#define NFC_3_2_STATUS_SUM_NAND_SUM_MSK (0xFF << 0)
+#define NFC_3_2_STATUS_SUM_ECC_SUM_POS (8)
+#define NFC_3_2_STATUS_SUM_ECC_SUM_MSK (0xFF << 8)
+
+#define NFC_3_2_LAUNCH_REG_OFF (0x40)
+#define NFC_3_2_LAUNCH_FCMD_POS (0)
+#define NFC_3_2_LAUNCH_FCMD_MSK (0x1 << 0)
+#define NFC_3_2_LAUNCH_FADD_POS (1)
+#define NFC_3_2_LAUNCH_FADD_MSK (0x1 << 1)
+#define NFC_3_2_LAUNCH_FDI_POS (2)
+#define NFC_3_2_LAUNCH_FDI_MSK (0x1 << 2)
+#define NFC_3_2_LAUNCH_FDO_POS (3)
+#define NFC_3_2_LAUNCH_FDO_MSK (0x7 << 3)
+#define NFC_3_2_LAUNCH_AUTO_PROG_POS (6)
+#define NFC_3_2_LAUNCH_AUTO_PROG_MSK (0x1 << 6)
+#define NFC_3_2_LAUNCH_AUTO_READ_POS (7)
+#define NFC_3_2_LAUNCH_AUTO_READ_MSK (0x1 << 7)
+#define NFC_3_2_LAUNCH_AUTO_ERASE_POS (9)
+#define NFC_3_2_LAUNCH_AUTO_ERASE_MSK (0x1 << 9)
+#define NFC_3_2_LAUNCH_COPY_BACK0_POS (10)
+#define NFC_3_2_LAUNCH_COPY_BACK0_MSK (0x1 << 10)
+#define NFC_3_2_LAUNCH_COPY_BACK1_POS (11)
+#define NFC_3_2_LAUNCH_COPY_BACK1_MSK (0x1 << 11)
+#define NFC_3_2_LAUNCH_AUTO_STATUS_POS (12)
+#define NFC_3_2_LAUNCH_AUTO_STATUS_MSK (0x1 << 12)
+
+#define NFC_3_2_WRPROT_REG_OFF (0x00)
+#define NFC_3_2_WRPROT_WPC_POS (0)
+#define NFC_3_2_WRPROT_WPC_MSK (0x7 << 0)
+#define NFC_3_2_WRPROT_CS2L_POS (3)
+#define NFC_3_2_WRPROT_CS2L_MSK (0x7 << 3)
+#define NFC_3_2_WRPROT_BLS_POS (6)
+#define NFC_3_2_WRPROT_BLS_MSK (0x3 << 6)
+#define NFC_3_2_WRPROT_LTS0_POS (8)
+#define NFC_3_2_WRPROT_LTS0_MSK (0x1 << 8)
+#define NFC_3_2_WRPROT_LS0_POS (9)
+#define NFC_3_2_WRPROT_LS0_MSK (0x1 << 9)
+#define NFC_3_2_WRPROT_US0_POS (10)
+#define NFC_3_2_WRPROT_US0_MSK (0x1 << 10)
+#define NFC_3_2_WRPROT_LTS1_POS (11)
+#define NFC_3_2_WRPROT_LTS1_MSK (0x1 << 11)
+#define NFC_3_2_WRPROT_LS1_POS (12)
+#define NFC_3_2_WRPROT_LS1_MSK (0x1 << 12)
+#define NFC_3_2_WRPROT_US1_POS (13)
+#define NFC_3_2_WRPROT_US1_MSK (0x1 << 13)
+#define NFC_3_2_WRPROT_LTS2_POS (14)
+#define NFC_3_2_WRPROT_LTS2_MSK (0x1 << 14)
+#define NFC_3_2_WRPROT_LS2_POS (15)
+#define NFC_3_2_WRPROT_LS2_MSK (0x1 << 15)
+#define NFC_3_2_WRPROT_US2_POS (16)
+#define NFC_3_2_WRPROT_US2_MSK (0x1 << 16)
+#define NFC_3_2_WRPROT_LTS3_POS (17)
+#define NFC_3_2_WRPROT_LTS3_MSK (0x1 << 17)
+#define NFC_3_2_WRPROT_LS3_POS (18)
+#define NFC_3_2_WRPROT_LS3_MSK (0x1 << 18)
+#define NFC_3_2_WRPROT_US3_POS (19)
+#define NFC_3_2_WRPROT_US3_MSK (0x1 << 19)
+#define NFC_3_2_WRPROT_LTS4_POS (20)
+#define NFC_3_2_WRPROT_LTS4_MSK (0x1 << 20)
+#define NFC_3_2_WRPROT_LS4_POS (21)
+#define NFC_3_2_WRPROT_LS4_MSK (0x1 << 21)
+#define NFC_3_2_WRPROT_US4_POS (22)
+#define NFC_3_2_WRPROT_US4_MSK (0x1 << 22)
+#define NFC_3_2_WRPROT_LTS5_POS (23)
+#define NFC_3_2_WRPROT_LTS5_MSK (0x1 << 23)
+#define NFC_3_2_WRPROT_LS5_POS (24)
+#define NFC_3_2_WRPROT_LS5_MSK (0x1 << 24)
+#define NFC_3_2_WRPROT_US5_POS (25)
+#define NFC_3_2_WRPROT_US5_MSK (0x1 << 25)
+#define NFC_3_2_WRPROT_LTS6_POS (26)
+#define NFC_3_2_WRPROT_LTS6_MSK (0x1 << 26)
+#define NFC_3_2_WRPROT_LS6_POS (27)
+#define NFC_3_2_WRPROT_LS6_MSK (0x1 << 27)
+#define NFC_3_2_WRPROT_US6_POS (28)
+#define NFC_3_2_WRPROT_US6_MSK (0x1 << 28)
+#define NFC_3_2_WRPROT_LTS7_POS (29)
+#define NFC_3_2_WRPROT_LTS7_MSK (0x1 << 29)
+#define NFC_3_2_WRPROT_LS7_POS (30)
+#define NFC_3_2_WRPROT_LS7_MSK (0x1 << 30)
+#define NFC_3_2_WRPROT_US7_POS (31)
+#define NFC_3_2_WRPROT_US7_MSK (0x1 << 31)
+
+#define NFC_3_2_UNLOCK_BLK_ADD0_REG_OFF (0x04)
+#define NFC_3_2_UNLOCK_BLK_ADD0_USBA0_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD0_USBA0_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD0_UEBA0_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD0_UEBA0_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD1_REG_OFF (0x08)
+#define NFC_3_2_UNLOCK_BLK_ADD1_USBA1_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD1_USBA1_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD1_UEBA1_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD1_UEBA1_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD2_REG_OFF (0x0C)
+#define NFC_3_2_UNLOCK_BLK_ADD2_USBA2_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD2_USBA2_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD2_UEBA2_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD2_UEBA2_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD3_REG_OFF (0x10)
+#define NFC_3_2_UNLOCK_BLK_ADD3_USBA3_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD3_USBA3_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD3_UEBA3_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD3_UEBA3_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD4_REG_OFF (0x14)
+#define NFC_3_2_UNLOCK_BLK_ADD4_USBA4_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD4_USBA4_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD4_UEBA4_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD4_UEBA4_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD5_REG_OFF (0x18)
+#define NFC_3_2_UNLOCK_BLK_ADD5_USBA5_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD5_USBA5_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD5_UEBA5_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD5_UEBA5_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD6_REG_OFF (0x1C)
+#define NFC_3_2_UNLOCK_BLK_ADD6_USBA6_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD6_USBA6_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD6_UEBA6_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD6_UEBA6_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD7_REG_OFF (0x20)
+#define NFC_3_2_UNLOCK_BLK_ADD7_USBA7_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD7_USBA7_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD7_UEBA7_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD7_UEBA7_MSK (0xFFFF<<16)
+
+#define NFC_3_2_CONFIG2_REG_OFF (0x24)
+#define NFC_3_2_CONFIG2_PS_POS (0)
+#define NFC_3_2_CONFIG2_PS_MSK (0x3 << 0)
+#define NFC_3_2_CONFIG2_SYM_POS (2)
+#define NFC_3_2_CONFIG2_SYM_MSK (0x1 << 2)
+#define NFC_3_2_CONFIG2_ECC_EN_POS (3)
+#define NFC_3_2_CONFIG2_ECC_EN_MSK (0x1 << 3)
+#define NFC_3_2_CONFIG2_CMD_PHASES_POS (4)
+#define NFC_3_2_CONFIG2_CMD_PHASES_MSK (0x1 << 4)
+#define NFC_3_2_CONFIG2_ADDR_PHASES0_POS (5)
+#define NFC_3_2_CONFIG2_ADDR_PHASES0_MSK (0x1 << 5)
+#define NFC_3_2_CONFIG2_ECC_MODE_POS (6)
+#define NFC_3_2_CONFIG2_ECC_MODE_MSK (0x1 << 6)
+#define NFC_3_2_CONFIG2_PPB_POS (7)
+#define NFC_3_2_CONFIG2_PPB_MSK (0x3 << 7)
+#define NFC_3_2_CONFIG2_EDC_POS (9)
+#define NFC_3_2_CONFIG2_EDC_MSK (0x7 << 9)
+#define NFC_3_2_CONFIG2_ADDR_PHASES1_POS (12)
+#define NFC_3_2_CONFIG2_ADDR_PHASES1_MSK (0x3 << 12)
+#define NFC_3_2_CONFIG2_AUTO_DONE_MSK_POS (14)
+#define NFC_3_2_CONFIG2_AUTO_DONE_MSK_MSK (0x1 << 14)
+#define NFC_3_2_CONFIG2_INT_MSK_POS (15)
+#define NFC_3_2_CONFIG2_INT_MSK_MSK (0x1 << 15)
+#define NFC_3_2_CONFIG2_SPAS_POS (16)
+#define NFC_3_2_CONFIG2_SPAS_MSK (0xFF << 16)
+#define NFC_3_2_CONFIG2_ST_CMD_POS (24)
+#define NFC_3_2_CONFIG2_ST_CMD_MSK (0xFF << 24)
+
+#define NFC_3_2_CONFIG3_REG_OFF (0x28)
+#define NFC_3_2_CONFIG3_ADD_OP_POS (0)
+#define NFC_3_2_CONFIG3_ADD_OP_MSK (0x3 << 0)
+#define NFC_3_2_CONFIG3_TOO_POS (2)
+#define NFC_3_2_CONFIG3_TOO_MSK (0x1 << 2)
+#define NFC_3_2_CONFIG3_FW_POS (3)
+#define NFC_3_2_CONFIG3_FW_MSK (0x1 << 3)
+#define NFC_3_2_CONFIG3_SB2R_POS (4)
+#define NFC_3_2_CONFIG3_SB2R_MSK (0x7 << 4)
+#define NFC_3_2_CONFIG3_NF_BIG_POS (7)
+#define NFC_3_2_CONFIG3_NF_BIG_MSK (0x1 << 7)
+#define NFC_3_2_CONFIG3_SBB_POS (8)
+#define NFC_3_2_CONFIG3_SBB_MSK (0x7 << 8)
+#define NFC_3_2_CONFIG3_DMA_MODE_POS (11)
+#define NFC_3_2_CONFIG3_DMA_MODE_MSK (0x1 << 11)
+#define NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS (12)
+#define NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK (0x7 << 12)
+#define NFC_3_2_CONFIG3_RBB_MODE_POS (15)
+#define NFC_3_2_CONFIG3_RBB_MODE_MSK (0x1 << 15)
+#define NFC_3_2_CONFIG3_FMP_POS (16)
+#define NFC_3_2_CONFIG3_FMP_MSK (0xF << 16)
+#define NFC_3_2_CONFIG3_NO_SDMA_POS (20)
+#define NFC_3_2_CONFIG3_NO_SDMA_MSK (0x1 << 20)
+
+#define NFC_3_2_IPC_REG_OFF (0x2C)
+#define NFC_3_2_IPC_CREQ_POS (0)
+#define NFC_3_2_IPC_CREQ_MSK (0x1 << 0)
+#define NFC_3_2_IPC_CACK_POS (1)
+#define NFC_3_2_IPC_CACK_MSK (0x1 << 1)
+#define NFC_3_2_IPC_DMA_STATUS_POS (26)
+#define NFC_3_2_IPC_DMA_STATUS_MSK (0x3 << 26)
+#define NFC_3_2_IPC_RB_B_POS (28)
+#define NFC_3_2_IPC_RB_B_MSK (0x1 << 28)
+#define NFC_3_2_IPC_LPS_POS (29)
+#define NFC_3_2_IPC_LPS_MSK (0x1 << 29)
+#define NFC_3_2_IPC_AUTO_PROG_DONE_POS (30)
+#define NFC_3_2_IPC_AUTO_PROG_DONE_MSK (0x1 << 30)
+#define NFC_3_2_IPC_INT_POS (31)
+#define NFC_3_2_IPC_INT_MSK (0x1 << 31)
+
+#define NFC_3_2_AXI_ERR_ADD_REG_OFF (0x30)
+
+/**
+ * enum override - Choices for overrides.
+ *
+ * Some functions of this driver can be overriden at run time. This is a
+ * convenient enumerated type for all such options.
+ */
+
+enum override {
+ NEVER = -1,
+ DRIVER_CHOICE = 0,
+ ALWAYS = 1,
+};
+
+/**
+ * struct physical_geometry - Physical geometry description.
+ *
+ * This structure describes the physical geometry of the medium.
+ *
+ * @chip_count: The number of chips in the medium.
+ * @chip_size: The size, in bytes, of a single chip (excluding the
+ * out-of-band bytes).
+ * @block_size: The size, in bytes, of a single block (excluding the
+ * out-of-band bytes).
+ * @page_data_size: The size, in bytes, of the data area in a page (excluding
+ * the out-of-band bytes).
+ * @page_oob_size: The size, in bytes, of the out-of-band area in a page.
+ */
+
+struct physical_geometry {
+ unsigned int chip_count;
+ uint64_t chip_size;
+ unsigned int block_size;
+ unsigned int page_data_size;
+ unsigned int page_oob_size;
+};
+
+/**
+ * struct nfc_geometry - NFC geometry description.
+ *
+ * This structure describes the NFC's view of the medium geometry, which may be
+ * different from the physical geometry (for example, we treat pages that are
+ * physically 2K+112 as if they are 2K+64).
+ *
+ * @page_data_size: The size of the data area in a page (excluding the
+ * out-of-band bytes). This is almost certain to be the same
+ * as the physical data size.
+ * @page_oob_size: The size of the out-of-band area in a page. This may be
+ * different from the physical OOB size.
+ * @ecc_algorithm: The name of the ECC algorithm (e.g., "Reed-Solomon" or
+ * "BCH").
+ * @ecc_strength: A number that describes the strength of the ECC algorithm.
+ * For example, various i.MX SoC's support ECC-1, ECC-4 or
+ * ECC-8 of the Reed-Solomon ECC algorithm.
+ * @buffer_count: The number of main/spare buffers used with this geometry.
+ * @spare_buf_size: The number of bytes held in each spare buffer.
+ * @spare_buf_spill: The number of extra bytes held in the last spare buffer.
+ * @mtd_layout: The MTD layout that best matches the geometry described by
+ * the rest of this structure. The logical layer might not use
+ * this structure, especially when interleaving.
+ */
+
+struct nfc_geometry {
+ const unsigned int page_data_size;
+ const unsigned int page_oob_size;
+ const char *ecc_algorithm;
+ const int ecc_strength;
+ const unsigned int buffer_count;
+ const unsigned int spare_buf_size;
+ const unsigned int spare_buf_spill;
+ struct nand_ecclayout mtd_layout;
+};
+
+/**
+ * struct logical_geometry - Logical geometry description.
+ *
+ * This structure describes the logical geometry we expose to MTD. This geometry
+ * may be different from the physical or NFC geometries, especially when
+ * interleaving.
+ *
+ * @chip_count: The number of chips in the medium.
+ * @chip_size: The size, in bytes, of a single chip (excluding the
+ * out-of-band bytes).
+ * @usable_size: The size, in bytes, of the medium that MTD can actually
+ * use. This may be less than the chip size multiplied by the
+ * number of chips.
+ * @block_size: The size, in bytes, of a single block (excluding the
+ * out-of-band bytes).
+ * @page_data_size: The size of the data area in a page (excluding the
+ * out-of-band bytes).
+ * @page_oob_size: The size of the out-of-band area in a page.
+ */
+
+struct logical_geometry {
+ unsigned int chip_count;
+ uint32_t chip_size;
+ uint32_t usable_size;
+ unsigned int block_size;
+ unsigned int page_data_size;
+ unsigned int page_oob_size;
+ struct nand_ecclayout *mtd_layout;
+};
+
+/**
+ * struct imx_nfc_data - i.MX NFC per-device data.
+ *
+ * Note that the "device" managed by this driver represents the NAND Flash
+ * controller *and* the NAND Flash medium behind it. Thus, the per-device data
+ * structure has information about the controller, the chips to which it is
+ * connected, and properties of the medium as a whole.
+ *
+ * @dev: A pointer to the owning struct device.
+ * @pdev: A pointer to the owning struct platform_device.
+ * @pdata: A pointer to the device's platform data.
+ * @buffers: A pointer to the NFC buffers.
+ * @primary_regs: A pointer to the NFC primary registers.
+ * @secondary_regs: A pointer to the NFC secondary registers.
+ * @clock: A pointer to the NFC struct clk.
+ * @interrupt: The NFC interrupt number.
+ * @physical_geometry: A description of the medium's physical geometry.
+ * @nfc: A pointer to the NFC HAL.
+ * @nfc_geometry: A description of the medium geometry as viewed by the
+ * NFC.
+ * @done: The struct completion we use to handle interrupts.
+ * @logical_geometry: A description of the logical geometry exposed to MTD.
+ * @interrupt_override: Can override how the driver uses interrupts when
+ * waiting for the NFC.
+ * @auto_op_override: Can override whether the driver uses automatic NFC
+ * operations.
+ * @inject_ecc_error: Indicates that the driver should inject an ECC error in
+ * the next read operation that uses ECC. User space
+ * programs can set this value through the sysfs node of
+ * the same name. If this value is less than zero, the
+ * driver will inject an uncorrectable ECC error. If this
+ * value is greater than zero, the driver will inject that
+ * number of correctable errors, capped at whatever
+ * possible maximum currently applies.
+ * @current_chip: The chip currently selected by the NAND Fash MTD HIL.
+ * A negative value indicates that no chip is selected.
+ * We use this field to detect when the HIL begins and
+ * ends essential transactions. This helps us to know when
+ * we should turn the NFC clock on or off.
+ * @command: The last command the HIL tried to send by calling
+ * cmdfunc(). Later functions use this information to
+ * adjust their behavior. The sentinel value ~0 indicates
+ * no command.
+ * @command_is_new: Indicates the command has just come down to cmdfunc()
+ * from the HIL and hasn't yet been handled. Other
+ * functions use this to adjust their behavior.
+ * @page_address: The current page address of interest. For reads, this
+ * information arrives in calls to cmdfunc(), but we don't
+ * actually use it until later.
+ * @nand: The data structure that represents this NAND Flash
+ * medium to the MTD NAND Flash system.
+ * @mtd: The data structure that represents this NAND Flash
+ * medium to MTD.
+ * @partitions: A pointer to a set of partitions collected from one of
+ * several possible sources (e.g., the boot loader, the
+ * kernel command line, etc.). See the global variable
+ * partition_source_types for the list of partition
+ * sources we examine. If this member is NULL, then no
+ * partitions were discovered.
+ * @partition_count: The number of discovered partitions.
+ */
+
+struct imx_nfc_data {
+
+ /* System Interface */
+ struct device *dev;
+ struct platform_device *pdev;
+
+ /* Platform Configuration */
+ struct imx_nfc_platform_data *pdata;
+ void *buffers;
+ void *primary_regs;
+ void *secondary_regs;
+ struct clk *clock;
+ unsigned int interrupt;
+
+ /* Flash Hardware */
+ struct physical_geometry physical_geometry;
+
+ /* NFC HAL and NFC Utilities */
+ struct nfc_hal *nfc;
+ struct nfc_geometry *nfc_geometry;
+ struct completion done;
+
+ /* Medium Abstraction Layer */
+ struct logical_geometry logical_geometry;
+ enum override interrupt_override;
+ enum override auto_op_override;
+ int inject_ecc_error;
+
+ /* MTD Interface Layer */
+ int current_chip;
+ unsigned int command;
+ int command_is_new;
+ int page_address;
+
+ /* NAND Flash MTD */
+ struct nand_chip nand;
+
+ /* MTD */
+ struct mtd_info mtd;
+ struct mtd_partition *partitions;
+ unsigned int partition_count;
+
+};
+
+/**
+ * struct nfc_hal - i.MX NFC HAL
+ *
+ * This structure embodies an abstract interface to the underlying NFC hardware.
+ *
+ * @major_version: The major version number of the NFC to which this
+ * structure applies.
+ * @minor_version: The minor version number of the NFC to which this
+ * structure applies.
+ * @max_chip_count: The maximum number of chips the NFC can possibly
+ * support. This may *not* be the actual number of chips
+ * currently connected. This value is constant for NFC's
+ * of a given version.
+ * @max_buffer_count: The number of main/spare buffers available in the NFC's
+ * memory. This value is constant for NFC's of a given
+ * version.
+ * @spare_buf_stride: The stride, in bytes, from the beginning of one spare
+ * buffer to the beginning of the next one. This value is
+ * constant for NFC's of a given version.
+ * @has_secondary_regs: Indicates if the NFC has a secondary register set that
+ * must be mapped in.
+ * @can_be_symmetric: Indicates if the NFC supports a "symmetric" clock. When
+ * the clock is "symmetric," the hardware waits one NFC
+ * clock for every read/write cycle. When the clock is
+ * "asymmetric," the hardware waits two NFC clocks for
+ * every read/write cycle.
+ * @init: Initializes the NFC and any version-specific data
+ * structures. This function will be called after
+ * everything has been set up for communication with the
+ * NFC itself, but before the platform has set up off-chip
+ * communication. Thus, this function must not attempt to
+ * communicate with the NAND Flash hardware. A non-zero
+ * return value indicates failure.
+ * @set_geometry: Based on the physical geometry, this function selects
+ * an NFC geometry structure and configures the NFC
+ * hardware to match. A non-zero return value indicates
+ * failure.
+ * @exit: Shuts down the NFC and cleans up version-specific data
+ * structures. This function will be called after the
+ * platform has shut down off-chip communication but while
+ * communication with the NFC still works.
+ * @set_closest_cycle: Configures the hardware to make the NAND Flash bus
+ * cycle period as close as possible to the given cycle
+ * period. This function is called during boot up and may
+ * assume that, at the time it's called, the parent clock
+ * is running at the highest rate it will ever run. Thus,
+ * this function need never worry that the NAND Flash bus
+ * will run faster and potentially make it impossible to
+ * communicate with the NAND Flash device -- it will only
+ * run slower.
+ * @mask_interrupt: Masks the NFC's interrupt.
+ * @unmask_interrupt: Unmasks the NFC's interrupt.
+ * @clear_interrupt: Clears the NFC's interrupt.
+ * @is_interrupting: Returns true if the NFC is interrupting.
+ * @is_ready: Returns true if all the chips in the medium are ready.
+ * This member may be set to NULL, which indicates that
+ * the underlying NFC hardware doesn't expose ready/busy
+ * signals.
+ * @set_force_ce: If passed true, forces the hardware chip enable signal
+ * for the current chip to be asserted always. If passed
+ * false, causes the chip enable signal to be asserted
+ * only during I/O.
+ * @set_ecc: Sets ECC on or off.
+ * @get_ecc_status: Examines the hardware ECC status and returns:
+ * == 0 => No errors.
+ * > 0 => The number of corrected errors.
+ * < 0 => There were uncorrectable errors.
+ * @get_symmetric: Gets the current symmetric clock setting. For versions
+ * that don't support symmetric clocks, this function
+ * always returns false.
+ * @set_symmetric: For versions that support symmetric clocks, sets
+ * whether or not the clock is symmetric.
+ * @select_chip: Selects the current chip.
+ * @command_cycle: Sends a command code and then returns immediately
+ * *without* waiting for the NFC to finish.
+ * @write_cycle: Applies a single write cycle to the current chip,
+ * sending the given byte, and waiting for the NFC to
+ * finish.
+ * @read_cycle: Applies a single read cycle to the current chip and
+ * returns the result, necessarily waiting for the NFC to
+ * finish. The width of the result is the same as the
+ * width of the Flash bus.
+ * @read_page: Applies read cycles to the current chip to read an
+ * entire page into the NFC. Note that ECC is enabled or
+ * disabled with the set_ecc function pointer (see above).
+ * This function waits for the NFC to finish before
+ * returning.
+ * @send_page: Applies write cycles to send an entire page from the
+ * NFC to the current chip. Note that ECC is enabled or
+ * disabled with the set_ecc function pointer (see above).
+ * This function waits for the NFC to finish before
+ * returning.
+ * @start_auto_read: Starts an automatic read operation. A NULL pointer
+ * indicates automatic read operations aren't available
+ * with this NFC version.
+ * @wait_for_auto_read: Blocks until an automatic read operation is ready for
+ * the CPU to copy a page out of the NFC.
+ * @resume_auto_read: Resumes an automatic read operation after the CPU has
+ * copied a page out.
+ * @start_auto_write: Starts an automatic write operation. A NULL pointer
+ * indicates automatic write operations aren't available
+ * with this NFC version.
+ * @wait_for_auto_write: Blocks until an automatic write operation is ready for
+ * the CPU to copy a page into the NFC.
+ * @start_auto_erase: Starts an automatic erase operation. A NULL pointer
+ * indicates automatic erase operations aren't available
+ * with this NFC version.
+ */
+
+struct nfc_hal {
+ const unsigned int major_version;
+ const unsigned int minor_version;
+ const unsigned int max_chip_count;
+ const unsigned int max_buffer_count;
+ const unsigned int spare_buf_stride;
+ const int has_secondary_regs;
+ const int can_be_symmetric;
+ int (*init) (struct imx_nfc_data *);
+ int (*set_geometry) (struct imx_nfc_data *);
+ void (*exit) (struct imx_nfc_data *);
+ int (*set_closest_cycle) (struct imx_nfc_data *, unsigned ns);
+ void (*mask_interrupt) (struct imx_nfc_data *);
+ void (*unmask_interrupt) (struct imx_nfc_data *);
+ void (*clear_interrupt) (struct imx_nfc_data *);
+ int (*is_interrupting) (struct imx_nfc_data *);
+ int (*is_ready) (struct imx_nfc_data *);
+ void (*set_force_ce) (struct imx_nfc_data *, int on);
+ void (*set_ecc) (struct imx_nfc_data *, int on);
+ int (*get_ecc_status) (struct imx_nfc_data *);
+ int (*get_symmetric) (struct imx_nfc_data *);
+ void (*set_symmetric) (struct imx_nfc_data *, int on);
+ void (*select_chip) (struct imx_nfc_data *, int chip);
+ void (*command_cycle) (struct imx_nfc_data *, unsigned cmd);
+ void (*write_cycle) (struct imx_nfc_data *, unsigned byte);
+ unsigned (*read_cycle) (struct imx_nfc_data *);
+ void (*read_page) (struct imx_nfc_data *);
+ void (*send_page) (struct imx_nfc_data *);
+ int (*start_auto_read) (struct imx_nfc_data *,
+ unsigned start, unsigned count, unsigned column, unsigned page);
+ int (*wait_for_auto_read) (struct imx_nfc_data *);
+ int (*resume_auto_read) (struct imx_nfc_data *);
+ int (*start_auto_write) (struct imx_nfc_data *,
+ unsigned start, unsigned count, unsigned column, unsigned page);
+ int (*wait_for_auto_write)(struct imx_nfc_data *);
+ int (*start_auto_erase) (struct imx_nfc_data *,
+ unsigned start, unsigned count, unsigned page);
+};
+
+/*
+ * This variable controls whether or not probing is enabled. If false, then
+ * the driver will refuse to probe. The "enable" module parameter controls the
+ * value of this variable.
+ */
+
+static int imx_nfc_module_enable = true;
+
+#ifdef EVENT_REPORTING
+
+/*
+ * This variable controls whether the driver reports event information by
+ * printing to the console. The "report_events" module parameter controls the
+ * value of this variable.
+ */
+
+static int imx_nfc_module_report_events; /* implicitly initialized false*/
+
+#endif
+
+/*
+ * This variable potentially overrides the driver's choice to interleave. The
+ * "interleave_override" module parameter controls the value of this variable.
+ */
+
+static enum override imx_nfc_module_interleave_override = DRIVER_CHOICE;
+
+/*
+ * When set, this variable forces the driver to use the bytewise copy functions
+ * to get data in and out of the NFC. This is intended for testing, not typical
+ * use.
+ */
+
+static int imx_nfc_module_force_bytewise_copy; /* implicitly initialized false*/
+
+/*
+ * The list of algorithms we use to get partition information from the
+ * environment.
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *partition_source_types[] = { "cmdlinepart", NULL };
+#endif
+
+/*
+ * The following structures describe the NFC geometries we support.
+ *
+ * Notice that pieces of some structure definitions are commented out and edited
+ * because various parts of the MTD system can't handle the way our hardware
+ * formats the out-of-band area.
+ *
+ * Here are the problems:
+ *
+ * - struct nand_ecclayout expects no more than 64 ECC bytes.
+ *
+ * The eccpos member of struct nand_ecclayout can't hold more than 64 ECC
+ * byte positions. Some of our formats have more so, unedited, they won't
+ * even compile. We comment out all ECC byte positions after the 64th one.
+ *
+ * - struct nand_ecclayout expects no more than 8 free spans.
+ *
+ * The oobfree member of struct nand_ecclayout can't hold more than 8 free
+ * spans. Some of our formats have more so, unedited, they won't even
+ * compile. We comment out all free spans after the eighth one.
+ *
+ * - The MEMGETOOBSEL command in the mtdchar driver.
+ *
+ * The mtdchar ioctl command MEMGETOOBSEL checks the number of ECC bytes
+ * against the length of the eccpos array in struct nand_oobinfo
+ * (see include/mtd/mtd-abi.h). This array can handle up to 32 ECC bytes,
+ * but some of our formats have more.
+ *
+ * To make this work, we cap the value assigned to eccbytes at 32.
+ *
+ * Notice that struct nand_oobinfo, used by mtdchar, is *different* from the
+ * struct nand_ecclayout that MTD uses internally. The latter structure
+ * can accomodate up to 64 ECC byte positions. Thus, we declare up to 64
+ * ECC byte positions here, even if the advertised number of ECC bytes is
+ * less.
+ *
+ * This command is obsolete and, if no one used it, we wouldn't care about
+ * this problem. Unfortunately The nandwrite program uses it, and everyone
+ * expects nandwrite to work (it's how everyone usually lays down their
+ * JFFS2 file systems).
+ */
+
+static struct nfc_geometry nfc_geometry_512_16_RS_ECC1 = {
+ .page_data_size = 512,
+ .page_oob_size = 16,
+ .ecc_algorithm = "Reed-Solomon",
+ .ecc_strength = 1,
+ .buffer_count = 1,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 5,
+ .eccpos = {6, 7, 8, 9, 10},
+ .oobavail = 11,
+ .oobfree = { {0, 6}, {11, 5} },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_512_16_RS_ECC4 = {
+ .page_data_size = 512,
+ .page_oob_size = 16,
+ .ecc_algorithm = "Reed-Solomon",
+ .ecc_strength = 4,
+ .buffer_count = 1,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 7,
+ .oobfree = { {0, 7} },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_512_16_BCH_ECC4 = {
+ .page_data_size = 512,
+ .page_oob_size = 16,
+ .ecc_algorithm = "BCH",
+ .ecc_strength = 4,
+ .buffer_count = 1,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 8,
+ .eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 8,
+ .oobfree = { {0, 8} },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_2K_64_RS_ECC4 = {
+ .page_data_size = 2048,
+ .page_oob_size = 64,
+ .ecc_algorithm = "Reed-Solomon",
+ .ecc_strength = 4,
+ .buffer_count = 4,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 32 /*9 * 4*/, /* See notes above. */
+ .eccpos = {
+
+ (0*16)+7 , (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
+ (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,
+
+ (1*16)+7 , (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
+ (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,
+
+ (2*16)+7 , (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
+ (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,
+
+ (3*16)+7 , (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
+ (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,
+
+ },
+ .oobavail = 7 * 4,
+ .oobfree = {
+ {(0*16)+0, 7},
+ {(1*16)+0, 7},
+ {(2*16)+0, 7},
+ {(3*16)+0, 7},
+ },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_2K_64_BCH_ECC4 = {
+ .page_data_size = 2048,
+ .page_oob_size = 64,
+ .ecc_algorithm = "BCH",
+ .ecc_strength = 4,
+ .buffer_count = 4,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 8 * 4,
+ .eccpos = {
+
+ (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
+ (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,
+
+ (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
+ (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,
+
+ (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
+ (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,
+
+ (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
+ (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,
+
+ },
+ .oobavail = 8 * 4,
+ .oobfree = {
+ {(0*16)+0, 8},
+ {(1*16)+0, 8},
+ {(2*16)+0, 8},
+ {(3*16)+0, 8},
+ },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_4K_128_BCH_ECC4 = {
+ .page_data_size = 4096,
+ .page_oob_size = 128,
+ .ecc_algorithm = "BCH",
+ .ecc_strength = 4,
+ .buffer_count = 8,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 8 * 8,
+ .eccpos = {
+
+ (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
+ (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,
+
+ (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
+ (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,
+
+ (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
+ (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,
+
+ (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
+ (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,
+
+ (4*16)+8 , (4*16)+9 , (4*16)+10, (4*16)+11,
+ (4*16)+12, (4*16)+13, (4*16)+14, (4*16)+15,
+
+ (5*16)+8 , (5*16)+9 , (5*16)+10, (5*16)+11,
+ (5*16)+12, (5*16)+13, (5*16)+14, (5*16)+15,
+
+ (6*16)+8 , (6*16)+9 , (6*16)+10, (6*16)+11,
+ (6*16)+12, (6*16)+13, (6*16)+14, (6*16)+15,
+
+ (7*16)+8 , (7*16)+9 , (7*16)+10, (7*16)+11,
+ (7*16)+12, (7*16)+13, (7*16)+14, (7*16)+15,
+
+ },
+ .oobavail = 8 * 8,
+ .oobfree = {
+ {(0*16)+0, 8},
+ {(1*16)+0, 8},
+ {(2*16)+0, 8},
+ {(3*16)+0, 8},
+ {(4*16)+0, 8},
+ {(5*16)+0, 8},
+ {(6*16)+0, 8},
+ {(7*16)+0, 8},
+ },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_4K_218_BCH_ECC8 = {
+ .page_data_size = 4096,
+ .page_oob_size = 218,
+ .ecc_algorithm = "BCH",
+ .ecc_strength = 8,
+ .buffer_count = 8,
+ .spare_buf_size = 26,
+ .spare_buf_spill = 10,
+ .mtd_layout = {
+ .eccbytes = 32 /*10 * 8*/, /* See notes above. */
+ .eccpos = {
+
+ (0*26)+12, (0*26)+13, (0*26)+14, (0*26)+15, (0*26)+16,
+ (0*26)+17, (0*26)+18, (0*26)+19, (0*26)+20, (0*26)+21,
+
+ (1*26)+12, (1*26)+13, (1*26)+14, (1*26)+15, (1*26)+16,
+ (1*26)+17, (1*26)+18, (1*26)+19, (1*26)+20, (1*26)+21,
+
+ (2*26)+12, (2*26)+13, (2*26)+14, (2*26)+15, (2*26)+16,
+ (2*26)+17, (2*26)+18, (2*26)+19, (2*26)+20, (2*26)+21,
+
+ (3*26)+12, (3*26)+13, (3*26)+14, (3*26)+15, (3*26)+16,
+ (3*26)+17, (3*26)+18, (3*26)+19, (3*26)+20, (3*26)+21,
+
+ (4*26)+12, (4*26)+13, (4*26)+14, (4*26)+15, (4*26)+16,
+ (4*26)+17, (4*26)+18, (4*26)+19, (4*26)+20, (4*26)+21,
+
+ (5*26)+12, (5*26)+13, (5*26)+14, (5*26)+15, (5*26)+16,
+ (5*26)+17, (5*26)+18, (5*26)+19, (5*26)+20, (5*26)+21,
+
+ (6*26)+12, (6*26)+13, (6*26)+14, (6*26)+15,
+ /* See notes above.
+ (6*26)+16,
+ (6*26)+17, (6*26)+18, (6*26)+19, (6*26)+20, (6*26)+21,
+
+ (7*26)+12, (7*26)+13, (7*26)+14, (7*26)+15, (7*26)+16,
+ (7*26)+17, (7*26)+18, (7*26)+19, (7*26)+20, (7*26)+21,
+ */
+
+ },
+ .oobavail = 96 /*(16 * 8) + 10*/, /* See notes above. */
+ .oobfree = {
+ {(0*26)+0, 12}, {(0*26)+22, 4},
+ {(1*26)+0, 12}, {(1*26)+22, 4},
+ {(2*26)+0, 12}, {(2*26)+22, 4},
+ {(3*26)+0, 48}, /* See notes above. */
+ /* See notes above.
+ {(3*26)+0, 12}, {(3*26)+22, 4},
+ {(4*26)+0, 12}, {(4*26)+22, 4},
+ {(5*26)+0, 12}, {(5*26)+22, 4},
+ {(6*26)+0, 12}, {(6*26)+22, 4},
+ {(7*26)+0, 12}, {(7*26)+22, 4 + 10},
+ */
+ },
+ }
+};
+
+/*
+ * When the logical geometry differs from the NFC geometry (e.g.,
+ * interleaving), we synthesize a layout rather than use the one that comes with
+ * the NFC geometry. See mal_set_logical_geometry().
+ */
+
+static struct nand_ecclayout synthetic_layout;
+
+/*
+ * These structures describe how the BBT code will find block marks in the OOB
+ * area of a page. Don't be confused by the fact that this is the same type used
+ * to describe bad block tables. Some of the same information is needed, so the
+ * designers use the same structure for two conceptually distinct functions.
+ */
+
+static uint8_t block_mark_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr small_page_block_mark_descriptor = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 5,
+ .len = 1,
+ .pattern = block_mark_pattern,
+};
+
+static struct nand_bbt_descr large_page_block_mark_descriptor = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 0,
+ .len = 2,
+ .pattern = block_mark_pattern,
+};
+
+/*
+ * Bad block table descriptors for the main and mirror tables.
+ */
+
+static uint8_t bbt_main_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t bbt_mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descriptor = {
+ .options =
+ NAND_BBT_LASTBLOCK |
+ NAND_BBT_CREATE |
+ NAND_BBT_WRITE |
+ NAND_BBT_2BIT |
+ NAND_BBT_VERSION |
+ NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_main_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descriptor = {
+ .options =
+ NAND_BBT_LASTBLOCK |
+ NAND_BBT_CREATE |
+ NAND_BBT_WRITE |
+ NAND_BBT_2BIT |
+ NAND_BBT_VERSION |
+ NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_mirror_pattern,
+};
+
+#ifdef EVENT_REPORTING
+
+/**
+ * struct event - A single record in the event trace.
+ *
+ * @time: The time at which the event occurred.
+ * @nesting: Indicates function call nesting.
+ * @description: A description of the event.
+ */
+
+struct event {
+ ktime_t time;
+ unsigned int nesting;
+ char *description;
+};
+
+/**
+ * The event trace.
+ *
+ * @overhead: The delay to take a time stamp and nothing else.
+ * @nesting: The current nesting level.
+ * @overflow: Indicates the trace overflowed.
+ * @next: Index of the next event to write.
+ * @events: The array of events.
+ */
+
+#define MAX_EVENT_COUNT (200)
+
+static struct {
+ ktime_t overhead;
+ int nesting;
+ int overflow;
+ unsigned int next;
+ struct event events[MAX_EVENT_COUNT];
+} event_trace;
+
+/**
+ * reset_event_trace() - Resets the event trace.
+ */
+static void reset_event_trace(void)
+{
+ event_trace.nesting = 0;
+ event_trace.overflow = false;
+ event_trace.next = 0;
+}
+
+/**
+ * add_event() - Adds an event to the event trace.
+ *
+ * @description: A description of the event.
+ * @delta: A delta to the nesting level for this event [-1, 0, 1].
+ */
+static inline void add_event(char *description, int delta)
+{
+ struct event *event;
+
+ if (!imx_nfc_module_report_events)
+ return;
+
+ if (event_trace.overflow)
+ return;
+
+ if (event_trace.next >= MAX_EVENT_COUNT) {
+ event_trace.overflow = true;
+ return;
+ }
+
+ event = event_trace.events + event_trace.next;
+
+ event->time = ktime_get();
+
+ event->description = description;
+
+ if (!delta)
+ event->nesting = event_trace.nesting;
+ else if (delta < 0) {
+ event->nesting = event_trace.nesting - 1;
+ event_trace.nesting -= 2;
+ } else {
+ event->nesting = event_trace.nesting + 1;
+ event_trace.nesting += 2;
+ }
+
+ if (event_trace.nesting < 0)
+ event_trace.nesting = 0;
+
+ event_trace.next++;
+
+}
+
+/**
+ * add_state_event_l() - Adds an event to display some state.
+ *
+ * @address: The address to examine.
+ * @mask: A mask to apply to the contents of the given address.
+ * @clear: An event message to add if the result is zero.
+ * @not_zero: An event message to add if the result is not zero.
+ */
+static void add_state_event_l(void *address, uint32_t mask,
+ char *zero, char *not_zero)
+{
+ int state;
+ state = !!(__raw_readl(address) & mask);
+ if (state)
+ add_event(not_zero, 0);
+ else
+ add_event(zero, 0);
+}
+
+/**
+ * start_event_trace() - Starts an event trace and adds the first event.
+ *
+ * @description: A description of the first event.
+ */
+static void start_event_trace(char *description)
+{
+
+ ktime_t t0;
+ ktime_t t1;
+
+ if (!imx_nfc_module_report_events)
+ return;
+
+ reset_event_trace();
+
+ t0 = ktime_get();
+ t1 = ktime_get();
+
+ event_trace.overhead = ktime_sub(t1, t0);
+
+ add_event(description, 1);
+
+}
+
+/**
+ * dump_event_trace() - Dumps the event trace.
+ */
+static void dump_event_trace(void)
+{
+ unsigned int i;
+ time_t seconds;
+ long nanoseconds;
+ char line[100];
+ int o;
+ struct event *first_event;
+ struct event *last_event;
+ struct event *matching_event;
+ struct event *event;
+ ktime_t delta;
+
+ /* Check if event reporting is turned off. */
+
+ if (!imx_nfc_module_report_events)
+ return;
+
+ /* Print important facts about this event trace. */
+
+ printk(KERN_DEBUG "\n+--------------\n");
+
+ printk(KERN_DEBUG "| Overhead : [%d:%d]\n",
+ event_trace.overhead.tv.sec,
+ event_trace.overhead.tv.nsec);
+
+ if (!event_trace.next) {
+ printk(KERN_DEBUG "| No Events\n");
+ return;
+ }
+
+ first_event = event_trace.events;
+ last_event = event_trace.events + (event_trace.next - 1);
+
+ delta = ktime_sub(last_event->time, first_event->time);
+ printk(KERN_DEBUG "| Elapsed Time: [%d:%d]\n",
+ delta.tv.sec, delta.tv.nsec);
+
+ if (event_trace.overflow)
+ printk(KERN_DEBUG "| Overflow!\n");
+
+ /* Print the events in this history. */
+
+ for (i = 0, event = event_trace.events;
+ i < event_trace.next; i++, event++) {
+
+ /* Get the delta between this event and the previous event. */
+
+ if (!i) {
+ seconds = 0;
+ nanoseconds = 0;
+ } else {
+ delta = ktime_sub(event[0].time, event[-1].time);
+ seconds = delta.tv.sec;
+ nanoseconds = delta.tv.nsec;
+ }
+
+ /* Print the current event. */
+
+ o = 0;
+
+ o = snprintf(line, sizeof(line) - o, "| [%ld:% 10ld]%*s %s",
+ seconds, nanoseconds,
+ event->nesting, "",
+ event->description);
+ /* Check if this is the last event in a nested series. */
+
+ if (i && (event[0].nesting < event[-1].nesting)) {
+
+ for (matching_event = event - 1;; matching_event--) {
+
+ if (matching_event < event_trace.events) {
+ matching_event = 0;
+ break;
+ }
+
+ if (matching_event->nesting == event->nesting)
+ break;
+
+ }
+
+ if (matching_event) {
+ delta = ktime_sub(event->time,
+ matching_event->time);
+ o += snprintf(line + o, sizeof(line) - o,
+ " <%d:%d]", delta.tv.sec,
+ delta.tv.nsec);
+ }
+
+ }
+
+ /* Check if this is the first event in a nested series. */
+
+ if ((i < event_trace.next - 1) &&
+ (event[0].nesting < event[1].nesting)) {
+
+ for (matching_event = event + 1;; matching_event++) {
+
+ if (matching_event >=
+ (event_trace.events+event_trace.next)) {
+ matching_event = 0;
+ break;
+ }
+
+ if (matching_event->nesting == event->nesting)
+ break;
+
+ }
+
+ if (matching_event) {
+ delta = ktime_sub(matching_event->time,
+ event->time);
+ o += snprintf(line + o, sizeof(line) - o,
+ " [%d:%d>", delta.tv.sec,
+ delta.tv.nsec);
+ }
+
+ }
+
+ printk(KERN_DEBUG "%s\n", line);
+
+ }
+
+ printk(KERN_DEBUG "+--------------\n");
+
+}
+
+/**
+ * stop_event_trace() - Stops an event trace.
+ *
+ * @description: A description of the last event.
+ */
+static void stop_event_trace(char *description)
+{
+ struct event *event;
+
+ if (!imx_nfc_module_report_events)
+ return;
+
+ /*
+ * We want the end of the trace, no matter what happens. If the trace
+ * has already overflowed, or is about to, just jam this event into the
+ * last spot. Otherwise, add this event like any other.
+ */
+
+ if (event_trace.overflow || (event_trace.next >= MAX_EVENT_COUNT)) {
+ event = event_trace.events + (MAX_EVENT_COUNT - 1);
+ event->time = ktime_get();
+ event->description = description;
+ event->nesting = 0;
+ } else {
+ add_event(description, -1);
+ }
+
+ dump_event_trace();
+ reset_event_trace();
+
+}
+
+#else /* EVENT_REPORTING */
+
+#define start_event_trace(description) do {} while (0)
+#define add_event(description, delta) do {} while (0)
+#define add_state_event_l(address, mask, zero, not_zero) do {} while (0)
+#define stop_event_trace(description) do {} while (0)
+#define dump_event_trace() do {} while (0)
+
+#endif /* EVENT_REPORTING */
+
+/**
+ * unimplemented() - Announces intentionally unimplemented features.
+ *
+ * @this: Per-device data.
+ * @msg: A message about the unimplemented feature.
+ */
+static inline void unimplemented(struct imx_nfc_data *this, const char * msg)
+{
+ dev_err(this->dev, "Intentionally unimplemented: %s", msg);
+}
+
+/**
+ * raw_read_mask_w() - Reads masked bits in a 16-bit hardware register.
+ */
+static inline uint16_t raw_read_mask_w(uint16_t mask, void *address)
+{
+ return __raw_readw(address) & mask;
+}
+
+/**
+ * raw_set_mask_w() - Sets bits in a 16-bit hardware register.
+ */
+static inline void raw_set_mask_w(uint16_t mask, void *address)
+{
+ __raw_writew(__raw_readw(address) | mask, address);
+}
+
+/**
+ * raw_clr_mask_w() - Clears bits in a 16-bit hardware register.
+ */
+static inline void raw_clr_mask_w(uint16_t mask, void *address)
+{
+ __raw_writew(__raw_readw(address) & (~mask), address);
+}
+
+/**
+ * raw_read_mask_l() - Reads masked bits in a 32-bit hardware register.
+ */
+static inline uint32_t raw_read_mask_l(uint32_t mask, void *address)
+{
+ return __raw_readl(address) & mask;
+}
+
+/**
+ * raw_set_mask_l() - Sets bits in a 32-bit hardware register.
+ */
+static inline void raw_set_mask_l(uint32_t mask, void *address)
+{
+ __raw_writel(__raw_readl(address) | mask, address);
+}
+
+/**
+ * raw_clr_mask_l() - Clears bits in a 32-bit hardware register.
+ */
+static inline void raw_clr_mask_l(uint32_t mask, void *address)
+{
+ __raw_writel(__raw_readl(address) & (~mask), address);
+}
+
+/**
+ * is_large_page_chip() - Returns true for large page media.
+ *
+ * @this: Per-device data.
+ */
+static inline int is_large_page_chip(struct imx_nfc_data *this)
+{
+ return (this->physical_geometry.page_data_size > 512);
+}
+
+/**
+ * is_small_page_chip() - Returns true for small page media.
+ *
+ * @this: Per-device data.
+ */
+static inline int is_small_page_chip(struct imx_nfc_data *this)
+{
+ return !is_large_page_chip(this);
+}
+
+/**
+ * get_cycle_in_ns() - Returns the given device's cycle period, in ns.
+ *
+ * @this: Per-device data.
+ */
+static inline unsigned get_cycle_in_ns(struct imx_nfc_data *this)
+{
+ unsigned long cycle_in_ns;
+
+ cycle_in_ns = 1000000000 / clk_get_rate(this->clock);
+
+ if (!this->nfc->get_symmetric(this))
+ cycle_in_ns *= 2;
+
+ return cycle_in_ns;
+
+}
+
+/**
+ * nfc_util_set_best_cycle() - Sets the closest possible NAND Flash bus cycle.
+ *
+ * This function computes the clock setup that will best approximate the given
+ * target Flash bus cycle period.
+ *
+ * For some NFC versions, we can make the clock "symmetric." When the clock
+ * is "symmetric," the hardware waits one NFC clock for every read/write cycle.
+ * When the clock is "asymmetric," the hardware waits two NFC clocks for every
+ * read/write cycle. Thus, making the clock asymmetric essentially divides the
+ * NFC clock by two.
+ *
+ * We compute the target frequency that matches the given target period. We then
+ * discover the closest available match with that frequency and the closest
+ * available match with double that frequency (for use with an asymmetric
+ * clock). We implement the best choice of original clock and symmetric or
+ * asymmetric setting, preferring symmetric clocks.
+ *
+ * @this: Per-device data.
+ * @ns: The target cycle period, in nanoseconds.
+ * @no_asym: Disallow making the clock asymmetric.
+ * @no_sym: Disallow making the clock symmetric.
+ */
+static int nfc_util_set_best_cycle(struct imx_nfc_data *this,
+ unsigned int ns, int no_asym, int no_sym)
+{
+ unsigned long target_hz;
+ long symmetric_hz;
+ long symmetric_delta_hz;
+ long asymmetric_hz;
+ long asymmetric_delta_hz;
+ unsigned long best_hz;
+ int best_symmetry_setting;
+ struct device *dev = this->dev;
+
+ /* The target cycle period must be greater than zero. */
+
+ if (!ns)
+ return -EINVAL;
+
+ /* Compute the target frequency. */
+
+ target_hz = 1000000000 / ns;
+
+ /* Find out how close we can get with a symmetric clock. */
+
+ if (!no_sym && this->nfc->can_be_symmetric)
+ symmetric_hz = clk_round_rate(this->clock, target_hz);
+ else
+ symmetric_hz = -EINVAL;
+
+ /* Find out how close we can get with an asymmetric clock. */
+
+ if (!no_asym)
+ asymmetric_hz = clk_round_rate(this->clock, target_hz * 2);
+ else
+ asymmetric_hz = -EINVAL;
+
+ /* Does anything work at all? */
+
+ if ((symmetric_hz == -EINVAL) && (asymmetric_hz == -EINVAL)) {
+ dev_err(dev, "Can't support Flash bus cycle of %uns\n", ns);
+ return -EINVAL;
+ }
+
+ /* Discover the best match. */
+
+ if ((symmetric_hz != -EINVAL) && (asymmetric_hz != -EINVAL)) {
+
+ symmetric_delta_hz = target_hz - symmetric_hz;
+ asymmetric_delta_hz = target_hz - (asymmetric_hz / 2);
+
+ if (symmetric_delta_hz <= asymmetric_delta_hz)
+ best_symmetry_setting = true;
+ else
+ best_symmetry_setting = false;
+
+ } else if (symmetric_hz != -EINVAL) {
+ best_symmetry_setting = true;
+ } else {
+ best_symmetry_setting = false;
+ }
+
+ best_hz = best_symmetry_setting ? symmetric_hz : asymmetric_hz;
+
+ /* Implement the best match. */
+
+ this->nfc->set_symmetric(this, best_symmetry_setting);
+
+ return clk_set_rate(this->clock, best_hz);
+
+}
+
+/**
+ * nfc_util_wait_for_the_nfc() - Waits for the NFC to finish an operation.
+ *
+ * @this: Per-device data.
+ * @use_irq: Indicates that we should wait for an interrupt rather than polling
+ * and delaying.
+ */
+static void nfc_util_wait_for_the_nfc(struct imx_nfc_data *this, int use_irq)
+{
+ unsigned spin_count;
+ struct device *dev = this->dev;
+
+ /* Apply the override, if any. */
+
+ switch (this->interrupt_override) {
+
+ case NEVER:
+ use_irq = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ use_irq = true;
+ break;
+
+ }
+
+ /* Check if we're using interrupts. */
+
+ if (use_irq) {
+
+ /*
+ * If control arrives here, the caller wants to use interrupts.
+ * Presumably, this operation is known to take a very long time.
+ */
+
+ if (this->nfc->is_interrupting(this)) {
+ add_event("Waiting for the NFC (early interrupt)", 1);
+ this->nfc->clear_interrupt(this);
+ } else {
+ add_event("Waiting for the NFC (interrupt)", 1);
+ this->nfc->unmask_interrupt(this);
+ wait_for_completion(&this->done);
+ }
+
+ add_event("NFC done", -1);
+
+ } else {
+
+ /*
+ * If control arrives here, the caller doesn't want to use
+ * interrupts. Presumably, this operation is too quick to
+ * justify the overhead. Leave the interrupt masked, and loop
+ * until the interrupt bit lights up, or we time out.
+ *
+ * We spin for a maximum of about 2ms before declaring a time
+ * out. No operation we could possibly spin on should take that
+ * long.
+ */
+
+ spin_count = 2000;
+
+ add_event("Waiting for the NFC (polling)", 1);
+
+ for (; spin_count > 0; spin_count--) {
+
+ if (this->nfc->is_interrupting(this)) {
+ this->nfc->clear_interrupt(this);
+ add_event("NFC done", -1);
+ return;
+ }
+
+ udelay(1);
+
+ }
+
+ /* Timed out. */
+
+ add_event("Timed out", -1);
+
+ dev_err(dev, "[wait_for_the_nfc] ===== Time Out =====\n");
+ dump_event_trace();
+
+ }
+
+}
+
+/**
+ * nfc_util_bytewise_copy_from_nfc_mem() - Copies bytes from the NFC memory.
+ *
+ * @from: A pointer to the source memory.
+ * @to: A pointer to the destination memory.
+ * @size: The number of bytes to copy.
+ */
+static void nfc_util_bytewise_copy_from_nfc_mem(const void *from,
+ void *to, size_t n)
+{
+ unsigned int i;
+ const uint8_t *f = from;
+ uint8_t *t = to;
+ uint16_t *p;
+ uint16_t x;
+
+ for (i = 0; i < n; i++, f++, t++) {
+
+ p = (uint16_t *) (((unsigned long) f) & ~((unsigned long) 1));
+
+ x = __raw_readw(p);
+
+ if (((unsigned long) f) & 0x1)
+ *t = (x >> 8) & 0xff;
+ else
+ *t = (x >> 0) & 0xff;
+
+ }
+
+}
+
+/**
+ * nfc_util_bytewise_copy_to_nfc_mem() - Copies bytes to the NFC memory.
+ *
+ * @from: A pointer to the source memory.
+ * @to: A pointer to the destination memory.
+ * @size: The number of bytes to copy.
+ */
+static void nfc_util_bytewise_copy_to_nfc_mem(const void *from,
+ void *to, size_t n)
+{
+ unsigned int i;
+ const uint8_t *f = from;
+ uint8_t *t = to;
+ uint16_t *p;
+ uint16_t x;
+
+ for (i = 0; i < n; i++, f++, t++) {
+
+ p = (uint16_t *) (((unsigned long) t) & ~((unsigned long) 1));
+
+ x = __raw_readw(p);
+
+ if (((unsigned long) t) & 0x1)
+ ((uint8_t *)(&x))[1] = *f;
+ else
+ ((uint8_t *)(&x))[0] = *f;
+
+ __raw_writew(x, p);
+
+ }
+
+}
+
+/**
+ * nfc_util_copy_from_nfc_mem() - Copies from the NFC memory to main memory.
+ *
+ * @from: A pointer to the source memory.
+ * @to: A pointer to the destination memory.
+ * @size: The number of bytes to copy.
+ */
+static void nfc_util_copy_from_nfc_mem(const void *from, void *to, size_t n)
+{
+ unsigned int chunk_count;
+
+ /*
+ * Check if we're testing bytewise copies.
+ */
+
+ if (imx_nfc_module_force_bytewise_copy)
+ goto force_bytewise_copy;
+
+ /*
+ * We'd like to use memcpy to get data out of the NFC but, because that
+ * memory responds only to 16- and 32-byte reads, we can only do so
+ * safely if both the start and end of both the source and destination
+ * are perfectly aligned on 4-byte boundaries.
+ */
+
+ if (!(((unsigned long) from) & 0x3) && !(((unsigned long) to) & 0x3)) {
+
+ /*
+ * If control arrives here, both the source and destination are
+ * aligned on 4-byte boundaries. Compute the number of whole,
+ * 4-byte chunks we can move.
+ */
+
+ chunk_count = n / 4;
+
+ /*
+ * Move all the chunks we can, and then update the pointers and
+ * byte count to show what's left.
+ */
+
+ if (chunk_count) {
+ memcpy(to, from, chunk_count * 4);
+ from += chunk_count * 4;
+ to += chunk_count * 4;
+ n -= chunk_count * 4;
+ }
+
+ }
+
+ /*
+ * Move what's left.
+ */
+
+force_bytewise_copy:
+
+ nfc_util_bytewise_copy_from_nfc_mem(from, to, n);
+
+}
+
+/**
+ * nfc_util_copy_to_nfc_mem() - Copies from main memory to the NFC memory.
+ *
+ * @from: A pointer to the source memory.
+ * @to: A pointer to the destination memory.
+ * @size: The number of bytes to copy.
+ */
+static void nfc_util_copy_to_nfc_mem(const void *from, void *to, size_t n)
+{
+ unsigned int chunk_count;
+
+ /*
+ * Check if we're testing bytewise copies.
+ */
+
+ if (imx_nfc_module_force_bytewise_copy)
+ goto force_bytewise_copy;
+
+ /*
+ * We'd like to use memcpy to get data into the NFC but, because that
+ * memory responds only to 16- and 32-byte writes, we can only do so
+ * safely if both the start and end of both the source and destination
+ * are perfectly aligned on 4-byte boundaries.
+ */
+
+ if (!(((unsigned long) from) & 0x3) && !(((unsigned long) to) & 0x3)) {
+
+ /*
+ * If control arrives here, both the source and destination are
+ * aligned on 4-byte boundaries. Compute the number of whole,
+ * 4-byte chunks we can move.
+ */
+
+ chunk_count = n / 4;
+
+ /*
+ * Move all the chunks we can, and then update the pointers and
+ * byte count to show what's left.
+ */
+
+ if (chunk_count) {
+ memcpy(to, from, chunk_count * 4);
+ from += chunk_count * 4;
+ to += chunk_count * 4;
+ n -= chunk_count * 4;
+ }
+
+ }
+
+ /*
+ * Move what's left.
+ */
+
+force_bytewise_copy:
+
+ nfc_util_bytewise_copy_to_nfc_mem(from, to, n);
+
+}
+
+/**
+ * nfc_util_copy_from_the_nfc() - Copies bytes out of the NFC.
+ *
+ * This function makes the data in the NFC look like a contiguous, model page.
+ *
+ * @this: Per-device data.
+ * @start: The index of the starting byte in the NFC.
+ * @buf: A pointer to the destination buffer.
+ * @len: The number of bytes to copy out.
+ */
+static void nfc_util_copy_from_the_nfc(struct imx_nfc_data *this,
+ unsigned int start, uint8_t *buf, unsigned int len)
+{
+ unsigned int i;
+ unsigned int count;
+ unsigned int offset;
+ unsigned int data_size;
+ unsigned int oob_size;
+ unsigned int total_size;
+ void *spare_base;
+ unsigned int first_spare;
+ void *from;
+ struct nfc_geometry *geometry = this->nfc_geometry;
+
+ /*
+ * During initialization, the HIL will attempt to read ID bytes. For
+ * some NFC hardware versions, the ID bytes are deposited in the NFC
+ * memory, so this function will be called to deliver them. At that
+ * point, we won't know the NFC geometry. That's OK because we're only
+ * going to be reading a byte at a time.
+ *
+ * If we don't yet know the NFC geometry, just plug in some values that
+ * make things work for now.
+ */
+
+ if (unlikely(!geometry)) {
+ data_size = NFC_MAIN_BUF_SIZE;
+ oob_size = 0;
+ } else {
+ data_size = geometry->page_data_size;
+ oob_size = geometry->page_oob_size;
+ }
+
+ total_size = data_size + oob_size;
+
+ /* Validate. */
+
+ if ((start >= total_size) || ((start + len) > total_size)) {
+ dev_err(this->dev, "Bad copy from NFC memory: [%u, %u]\n",
+ start, len);
+ return;
+ }
+
+ /* Check if we're copying anything at all. */
+
+ if (!len)
+ return;
+
+ /* Check if anything comes from the main area. */
+
+ if (start < data_size) {
+
+ /* Compute the bytes to copy from the main area. */
+
+ count = min(len, data_size - start);
+
+ /* Copy. */
+
+ nfc_util_copy_from_nfc_mem(this->buffers + start, buf, count);
+
+ buf += count;
+ start += count;
+ len -= count;
+
+ }
+
+ /* Check if we're done. */
+
+ if (!len)
+ return;
+
+ /* Compute the base address of the spare buffers. */
+
+ spare_base = this->buffers +
+ (this->nfc->max_buffer_count * NFC_MAIN_BUF_SIZE);
+
+ /* Discover in which spare buffer the copying begins. */
+
+ first_spare = (start - data_size) / geometry->spare_buf_size;
+
+ /* Check if anything comes from the regular spare buffer area. */
+
+ if (first_spare < geometry->buffer_count) {
+
+ /* Start copying from spare buffers. */
+
+ for (i = first_spare; i < geometry->buffer_count; i++) {
+
+ /* Compute the offset into this spare area. */
+
+ offset = start -
+ (data_size + (geometry->spare_buf_size * i));
+
+ /* Compute the address of that offset. */
+
+ from = spare_base + offset +
+ (this->nfc->spare_buf_stride * i);
+
+ /* Compute the bytes to copy from this spare area. */
+
+ count = min(len, geometry->spare_buf_size - offset);
+
+ /* Copy. */
+
+ nfc_util_copy_from_nfc_mem(from, buf, count);
+
+ buf += count;
+ start += count;
+ len -= count;
+
+ }
+
+ }
+
+ /* Check if we're done. */
+
+ if (!len)
+ return;
+
+ /* Compute the offset into the extra spare area. */
+
+ offset = start -
+ (data_size + (geometry->spare_buf_size*geometry->buffer_count));
+
+ /* Compute the address of that offset. */
+
+ from = spare_base + offset +
+ (this->nfc->spare_buf_stride * geometry->buffer_count);
+
+ /* Compute the bytes to copy from the extra spare area. */
+
+ count = min(len, geometry->spare_buf_spill - offset);
+
+ /* Copy. */
+
+ nfc_util_copy_from_nfc_mem(from, buf, count);
+
+}
+
+/**
+ * nfc_util_copy_to_the_nfc() - Copies bytes into the NFC memory.
+ *
+ * This function makes the data in the NFC look like a contiguous, model page.
+ *
+ * @this: Per-device data.
+ * @buf: A pointer to the source buffer.
+ * @start: The index of the starting byte in the NFC memory.
+ * @len: The number of bytes to copy in.
+ */
+static void nfc_util_copy_to_the_nfc(struct imx_nfc_data *this,
+ const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ unsigned int i;
+ unsigned int count;
+ unsigned int offset;
+ unsigned int data_size;
+ unsigned int oob_size;
+ unsigned int total_size;
+ void *spare_base;
+ unsigned int first_spare;
+ void *to;
+ struct nfc_geometry *geometry = this->nfc_geometry;
+
+ /* Establish some important facts. */
+
+ data_size = geometry->page_data_size;
+ oob_size = geometry->page_oob_size;
+ total_size = data_size + oob_size;
+
+ /* Validate. */
+
+ if ((start >= total_size) || ((start + len) > total_size)) {
+ dev_err(this->dev, "Bad copy to NFC memory: [%u, %u]\n",
+ start, len);
+ return;
+ }
+
+ /* Check if we're copying anything at all. */
+
+ if (!len)
+ return;
+
+ /* Check if anything goes to the main area. */
+
+ if (start < data_size) {
+
+ /* Compute the bytes to copy to the main area. */
+
+ count = min(len, data_size - start);
+
+ /* Copy. */
+
+ nfc_util_copy_to_nfc_mem(buf, this->buffers + start, count);
+
+ buf += count;
+ start += count;
+ len -= count;
+
+ }
+
+ /* Check if we're done. */
+
+ if (!len)
+ return;
+
+ /* Compute the base address of the spare buffers. */
+
+ spare_base = this->buffers +
+ (this->nfc->max_buffer_count * NFC_MAIN_BUF_SIZE);
+
+ /* Discover in which spare buffer the copying begins. */
+
+ first_spare = (start - data_size) / geometry->spare_buf_size;
+
+ /* Check if anything goes to the regular spare buffer area. */
+
+ if (first_spare < geometry->buffer_count) {
+
+ /* Start copying to spare buffers. */
+
+ for (i = first_spare; i < geometry->buffer_count; i++) {
+
+ /* Compute the offset into this spare area. */
+
+ offset = start -
+ (data_size + (geometry->spare_buf_size * i));
+
+ /* Compute the address of that offset. */
+
+ to = spare_base + offset +
+ (this->nfc->spare_buf_stride * i);
+
+ /* Compute the bytes to copy to this spare area. */
+
+ count = min(len, geometry->spare_buf_size - offset);
+
+ /* Copy. */
+
+ nfc_util_copy_to_nfc_mem(buf, to, count);
+
+ buf += count;
+ start += count;
+ len -= count;
+
+ }
+
+ }
+
+ /* Check if we're done. */
+
+ if (!len)
+ return;
+
+ /* Compute the offset into the extra spare area. */
+
+ offset = start -
+ (data_size + (geometry->spare_buf_size*geometry->buffer_count));
+
+ /* Compute the address of that offset. */
+
+ to = spare_base + offset +
+ (this->nfc->spare_buf_stride * geometry->buffer_count);
+
+ /* Compute the bytes to copy to the extra spare area. */
+
+ count = min(len, geometry->spare_buf_spill - offset);
+
+ /* Copy. */
+
+ nfc_util_copy_to_nfc_mem(buf, to, count);
+
+}
+
+/**
+ * nfc_util_isr() - i.MX NFC ISR.
+ *
+ * @irq: The arriving interrupt number.
+ * @context: A cookie for this ISR.
+ */
+static irqreturn_t nfc_util_isr(int irq, void *cookie)
+{
+ struct imx_nfc_data *this = cookie;
+ this->nfc->mask_interrupt(this);
+ this->nfc->clear_interrupt(this);
+ complete(&this->done);
+ return IRQ_HANDLED;
+}
+
+/**
+ * nfc_util_send_cmd() - Sends a command to the current chip, without waiting.
+ *
+ * @this: Per-device data.
+ * @command: The command code.
+ */
+
+static void nfc_util_send_cmd(struct imx_nfc_data *this, unsigned int command)
+{
+
+ add_event("Entering nfc_util_send_cmd", 1);
+
+ this->nfc->command_cycle(this, command);
+
+ add_event("Exiting nfc_util_send_cmd", -1);
+
+}
+
+/**
+ * nfc_util_send_cmd_and_addrs() - Sends a cmd and addrs to the current chip.
+ *
+ * This function conveniently combines sending a command, and then sending
+ * optional addresses, waiting for the NFC to finish will all steps.
+ *
+ * @this: Per-device data.
+ * @command: The command code.
+ * @column: The column address to send, or -1 if no column address applies.
+ * @page: The page address to send, or -1 if no page address applies.
+ */
+
+static void nfc_util_send_cmd_and_addrs(struct imx_nfc_data *this,
+ unsigned command, int column, int page)
+{
+ uint32_t page_mask;
+
+ add_event("Entering nfc_util_send_cmd_and_addrs", 1);
+
+ /* Send the command.*/
+
+ add_event("Sending the command...", 0);
+
+ nfc_util_send_cmd(this, command);
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+ /* Send the addresses. */
+
+ add_event("Sending the addresses...", 0);
+
+ if (column != -1) {
+
+ this->nfc->write_cycle(this, (column >> 0) & 0xff);
+
+ if (is_large_page_chip(this))
+ this->nfc->write_cycle(this, (column >> 8) & 0xff);
+
+ }
+
+ if (page != -1) {
+
+ page_mask = this->nand.pagemask;
+
+ do {
+ this->nfc->write_cycle(this, page & 0xff);
+ page_mask >>= 8;
+ page >>= 8;
+ } while (page_mask != 0);
+
+ }
+
+ add_event("Exiting nfc_util_send_cmd_and_addrs", -1);
+
+}
+
+/**
+ * nfc_2_x_exit() - Version-specific shut down.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_x_exit(struct imx_nfc_data *this)
+{
+}
+
+/**
+ * nfc_2_x_clear_interrupt() - Clears an interrupt.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_x_clear_interrupt(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+ raw_clr_mask_w(NFC_2_X_CONFIG2_INT_MSK, base + NFC_2_X_CONFIG2_REG_OFF);
+}
+
+/**
+ * nfc_2_x_is_interrupting() - Returns the interrupt bit status.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_x_is_interrupting(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+ return raw_read_mask_w(NFC_2_X_CONFIG2_INT_MSK,
+ base + NFC_2_X_CONFIG2_REG_OFF);
+}
+
+/**
+ * nfc_2_x_command_cycle() - Sends a command.
+ *
+ * @this: Per-device data.
+ * @command: The command code.
+ */
+static void nfc_2_x_command_cycle(struct imx_nfc_data *this, unsigned command)
+{
+ void *base = this->primary_regs;
+
+ /* Write the command we want to send. */
+
+ __raw_writew(command, base + NFC_2_X_FLASH_CMD_REG_OFF);
+
+ /* Launch a command cycle. */
+
+ __raw_writew(NFC_2_X_CONFIG2_FCMD_MSK, base + NFC_2_X_CONFIG2_REG_OFF);
+
+}
+
+/**
+ * nfc_2_x_write_cycle() - Writes a single byte.
+ *
+ * @this: Per-device data.
+ * @byte: The byte.
+ */
+static void nfc_2_x_write_cycle(struct imx_nfc_data *this, unsigned int byte)
+{
+ void *base = this->primary_regs;
+
+ /* Give the NFC the byte we want to write. */
+
+ __raw_writew(byte, base + NFC_2_X_FLASH_ADDR_REG_OFF);
+
+ /* Launch an address cycle.
+ *
+ * This is *sort* of a hack, but not really. The intent of the NFC
+ * design is for this operation to send an address byte. In fact, the
+ * NFC neither knows nor cares what we're sending. It justs runs a write
+ * cycle.
+ */
+
+ __raw_writew(NFC_2_X_CONFIG2_FADD_MSK, base + NFC_2_X_CONFIG2_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+}
+
+/**
+ * nfc_2_0_init() - Version-specific hardware initialization.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_0_init(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+
+ /* Initialize the interrupt machinery. */
+
+ this->nfc->mask_interrupt(this);
+ this->nfc->clear_interrupt(this);
+
+ /* Unlock the NFC memory. */
+
+ __raw_writew(0x2, base + NFC_2_X_CONFIG_REG_OFF);
+
+ /* Set the unlocked block range to cover the entire medium. */
+
+ __raw_writew(0 , base + NFC_2_0_UNLOCK_START_REG_OFF);
+ __raw_writew(~0, base + NFC_2_0_UNLOCK_END_REG_OFF);
+
+ /* Unlock all blocks. */
+
+ __raw_writew(0x4, base + NFC_2_X_WR_PROT_REG_OFF);
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * nfc_2_0_mask_interrupt() - Masks interrupts.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_mask_interrupt(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+ raw_set_mask_w(NFC_2_0_CONFIG1_INT_MSK_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+}
+
+/**
+ * nfc_2_0_unmask_interrupt() - Unmasks interrupts.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_unmask_interrupt(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+ raw_clr_mask_w(NFC_2_0_CONFIG1_INT_MSK_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+}
+
+/**
+ * nfc_2_0_set_ecc() - Turns ECC on or off.
+ *
+ * @this: Per-device data.
+ * @on: Indicates if ECC should be on or off.
+ */
+static void nfc_2_0_set_ecc(struct imx_nfc_data *this, int on)
+{
+ void *base = this->primary_regs;
+
+ if (on)
+ raw_set_mask_w(NFC_2_0_CONFIG1_ECC_EN_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+ else
+ raw_clr_mask_w(NFC_2_0_CONFIG1_ECC_EN_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_2_0_get_ecc_status() - Reports ECC errors.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_0_get_ecc_status(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ void *base = this->primary_regs;
+ uint16_t status_reg;
+ unsigned int buffer_status[4];
+ int status;
+
+ /* Get the entire status register. */
+
+ status_reg = __raw_readw(base + NFC_2_0_ECC_STATUS_REG_OFF);
+
+ /* Pick out the status for each buffer. */
+
+ buffer_status[0] = (status_reg & NFC_2_0_ECC_STATUS_NOSER1_MSK)
+ >> NFC_2_0_ECC_STATUS_NOSER1_POS;
+
+ buffer_status[1] = (status_reg & NFC_2_0_ECC_STATUS_NOSER2_MSK)
+ >> NFC_2_0_ECC_STATUS_NOSER2_POS;
+
+ buffer_status[2] = (status_reg & NFC_2_0_ECC_STATUS_NOSER3_MSK)
+ >> NFC_2_0_ECC_STATUS_NOSER3_POS;
+
+ buffer_status[3] = (status_reg & NFC_2_0_ECC_STATUS_NOSER4_MSK)
+ >> NFC_2_0_ECC_STATUS_NOSER4_POS;
+
+ /* Loop through the buffers we're actually using. */
+
+ status = 0;
+
+ for (i = 0; i < this->nfc_geometry->buffer_count; i++) {
+
+ if (buffer_status[i] > this->nfc_geometry->ecc_strength) {
+ status = -1;
+ break;
+ }
+
+ status += buffer_status[i];
+
+ }
+
+ /* Return the final result. */
+
+ return status;
+
+}
+
+/**
+ * nfc_2_0_get_symmetric() - Indicates if the clock is symmetric.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_0_get_symmetric(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+
+ return !!raw_read_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_2_0_set_symmetric() - Turns symmetric clock mode on or off.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_set_symmetric(struct imx_nfc_data *this, int on)
+{
+ void *base = this->primary_regs;
+
+ if (on)
+ raw_set_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+ else
+ raw_clr_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_2_0_set_geometry() - Configures for the medium geometry.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_0_set_geometry(struct imx_nfc_data *this)
+{
+ struct physical_geometry *physical = &this->physical_geometry;
+
+ /* Select an NFC geometry. */
+
+ switch (physical->page_data_size) {
+
+ case 512:
+ this->nfc_geometry = &nfc_geometry_512_16_RS_ECC4;
+ break;
+
+ case 2048:
+ this->nfc_geometry = &nfc_geometry_2K_64_RS_ECC4;
+ break;
+
+ default:
+ dev_err(this->dev, "NFC can't handle page size: %u",
+ physical->page_data_size);
+ return !0;
+ break;
+
+ }
+
+ /*
+ * This NFC version receives page size information from a register
+ * that's external to the NFC. We must rely on platform-specific code
+ * to set this register for us.
+ */
+
+ return this->pdata->set_page_size(physical->page_data_size);
+
+}
+
+/**
+ * nfc_2_0_select_chip() - Selects the current chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip number to select, or -1 to select no chip.
+ */
+static void nfc_2_0_select_chip(struct imx_nfc_data *this, int chip)
+{
+}
+
+/**
+ * nfc_2_0_read_cycle() - Applies a single read cycle to the current chip.
+ *
+ * @this: Per-device data.
+ */
+static unsigned int nfc_2_0_read_cycle(struct imx_nfc_data *this)
+{
+ uint8_t byte;
+ unsigned int result;
+ void *base = this->primary_regs;
+
+ /* Read into main buffer 0. */
+
+ __raw_writew(0x0, base + NFC_2_0_BUF_ADDR_REG_OFF);
+
+ /* Launch a "Data Out" operation. */
+
+ __raw_writew(0x4 << NFC_2_X_CONFIG2_FDO_POS,
+ base + NFC_2_X_CONFIG2_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+ /* Get the result from the NFC memory. */
+
+ nfc_util_copy_from_the_nfc(this, 0, &byte, 1);
+ result = byte;
+
+ /* Return the results. */
+
+ return result;
+
+}
+
+/**
+ * nfc_2_0_read_page() - Reads a page from the current chip into the NFC.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_read_page(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ void *base = this->primary_regs;
+
+ /* Loop over the number of buffers in use. */
+
+ for (i = 0; i < this->nfc_geometry->buffer_count; i++) {
+
+ /* Make the NFC read into the current buffer. */
+
+ __raw_writew(i << NFC_2_0_BUF_ADDR_RBA_POS,
+ base + NFC_2_0_BUF_ADDR_REG_OFF);
+
+ /* Launch a page data out operation. */
+
+ __raw_writew(0x1 << NFC_2_X_CONFIG2_FDO_POS,
+ base + NFC_2_X_CONFIG2_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ }
+
+}
+
+/**
+ * nfc_2_0_send_page() - Sends a page from the NFC to the current chip.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_send_page(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ void *base = this->primary_regs;
+
+ /* Loop over the number of buffers in use. */
+
+ for (i = 0; i < this->nfc_geometry->buffer_count; i++) {
+
+ /* Make the NFC send from the current buffer. */
+
+ __raw_writew(i << NFC_2_0_BUF_ADDR_RBA_POS,
+ base + NFC_2_0_BUF_ADDR_REG_OFF);
+
+ /* Launch a page data in operation. */
+
+ __raw_writew(0x1 << NFC_2_X_CONFIG2_FDI_POS,
+ base + NFC_2_X_CONFIG2_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ }
+
+}
+
+/**
+ * nfc_3_2_init() - Hardware initialization.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_init(struct imx_nfc_data *this)
+{
+ int error;
+ unsigned int no_sdma;
+ unsigned int fmp;
+ unsigned int rbb_mode;
+ unsigned int num_of_devices;
+ unsigned int dma_mode;
+ unsigned int sbb;
+ unsigned int nf_big;
+ unsigned int sb2r;
+ unsigned int fw;
+ unsigned int too;
+ unsigned int add_op;
+ uint32_t config3;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ /* Initialize the interrupt machinery. */
+
+ this->nfc->mask_interrupt(this);
+ this->nfc->clear_interrupt(this);
+
+ /* Set up the clock. */
+
+ error = this->nfc->set_closest_cycle(this,
+ this->pdata->target_cycle_in_ns);
+
+ if (error)
+ return !0;
+
+ /* We never read the spare area alone. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_SP_EN_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Tell the NFC the "Read Status" command code. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG2_ST_CMD_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+ raw_set_mask_l(NAND_CMD_STATUS << NFC_3_2_CONFIG2_ST_CMD_POS,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+ /*
+ * According to erratum ENGcm09051, the CONFIG3 register doesn't reset
+ * correctly, so we need to re-build the entire register just in case.
+ */
+
+ /*
+ * Set the NO_SDMA bit to tell the NFC that we are NOT using SDMA. If
+ * you clear this bit (to indicates you *are* using SDMA), but you
+ * don't actually set up SDMA, the NFC has been observed to crash the
+ * hardware when it asserts its DMA request signals. In the future, we
+ * *may* use SDMA, but it's not worth the effort at this writing.
+ */
+
+ no_sdma = 0x1;
+
+ /*
+ * Set the default FIFO Mode Protection (128 bytes). FMP doesn't work if
+ * the NO_SDMA bit is set.
+ */
+
+ fmp = 0x2;
+
+ /*
+ * The rbb_mode bit determines how the NFC figures out whether chips are
+ * ready during automatic operations only (this has no effect on atomic
+ * operations). The two choices are either to monitor the ready/busy
+ * signals, or to read the status register. We monitor the ready/busy
+ * signals.
+ */
+
+ rbb_mode = 0x1;
+
+ /*
+ * We don't yet know how many devices are connected. We'll find out in
+ * out in nfc_3_2_set_geometry().
+ */
+
+ num_of_devices = 0;
+
+ /* Set the default DMA mode. */
+
+ dma_mode = 0x0;
+
+ /* Set the default status busy bit. */
+
+ sbb = 0x6;
+
+ /* Little-endian (the default). */
+
+ nf_big = 0x0;
+
+ /* Set the default (standard) status bit to record. */
+
+ sb2r = 0x0;
+
+ /* We support only 8-bit Flash bus width. */
+
+ fw = 0x1;
+
+ /* We don't support "two-on-one." */
+
+ too = 0x0;
+
+ /* Set the addressing option. */
+
+ add_op = 0x3;
+
+ /* Set the CONFIG3 register. */
+
+ config3 = 0;
+
+ config3 |= no_sdma << NFC_3_2_CONFIG3_NO_SDMA_POS;
+ config3 |= fmp << NFC_3_2_CONFIG3_FMP_POS;
+ config3 |= rbb_mode << NFC_3_2_CONFIG3_RBB_MODE_POS;
+ config3 |= num_of_devices << NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
+ config3 |= dma_mode << NFC_3_2_CONFIG3_DMA_MODE_POS;
+ config3 |= sbb << NFC_3_2_CONFIG3_SBB_POS;
+ config3 |= nf_big << NFC_3_2_CONFIG3_NF_BIG_POS;
+ config3 |= sb2r << NFC_3_2_CONFIG3_SB2R_POS;
+ config3 |= fw << NFC_3_2_CONFIG3_FW_POS;
+ config3 |= too << NFC_3_2_CONFIG3_TOO_POS;
+ config3 |= add_op << NFC_3_2_CONFIG3_ADD_OP_POS;
+
+ __raw_writel(config3, secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * nfc_3_2_set_geometry() - Configures for the medium geometry.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_set_geometry(struct imx_nfc_data *this)
+{
+ unsigned int ps;
+ unsigned int cmd_phases;
+ unsigned int pages_per_chip;
+ unsigned int addr_phases0;
+ unsigned int addr_phases1;
+ unsigned int pages_per_block;
+ unsigned int ecc_mode;
+ unsigned int ppb;
+ unsigned int spas;
+ unsigned int mask;
+ uint32_t config2;
+ unsigned int num_of_devices;
+ uint32_t config3;
+ unsigned int x;
+ unsigned int chip;
+ struct physical_geometry *physical = &this->physical_geometry;
+ void *secondary_base = this->secondary_regs;
+
+ /*
+ * Select an NFC geometry based on the physical geometry and the
+ * capabilities of this NFC.
+ */
+
+ switch (physical->page_data_size) {
+
+ case 512:
+ this->nfc_geometry = &nfc_geometry_512_16_BCH_ECC4;
+ ps = 0;
+ break;
+
+ case 2048:
+ this->nfc_geometry = &nfc_geometry_2K_64_BCH_ECC4;
+ ps = 1;
+ break;
+
+ case 4096:
+
+ switch (this->physical_geometry.page_oob_size) {
+
+ case 128:
+ this->nfc_geometry = &nfc_geometry_4K_128_BCH_ECC4;
+ break;
+
+ case 218:
+ this->nfc_geometry = &nfc_geometry_4K_218_BCH_ECC8;
+ break;
+
+ default:
+ dev_err(this->dev,
+ "NFC can't handle page geometry: %u+%u",
+ physical->page_data_size,
+ physical->page_oob_size);
+ return !0;
+ break;
+
+ }
+
+ ps = 2;
+
+ break;
+
+ default:
+ dev_err(this->dev, "NFC can't handle page size: %u",
+ physical->page_data_size);
+ return !0;
+ break;
+
+ }
+
+ /* Compute the ECC mode. */
+
+ switch (this->nfc_geometry->ecc_strength) {
+
+ case 4:
+ ecc_mode = 0;
+ break;
+
+ case 8:
+ ecc_mode = 1;
+ break;
+
+ default:
+ dev_err(this->dev, "NFC can't handle ECC strength: %u",
+ this->nfc_geometry->ecc_strength);
+ return !0;
+ break;
+
+ }
+
+ /* Compute the pages per block. */
+
+ pages_per_block = physical->block_size / physical->page_data_size;
+
+ switch (pages_per_block) {
+ case 32:
+ ppb = 0;
+ break;
+ case 64:
+ ppb = 1;
+ break;
+ case 128:
+ ppb = 2;
+ break;
+ case 256:
+ ppb = 3;
+ break;
+ default:
+ dev_err(this->dev, "NFC can't handle pages per block: %d",
+ pages_per_block);
+ return !0;
+ break;
+ }
+
+ /*
+ * The hardware needs to know the physical size of the spare area, in
+ * units of half-words (16 bits). This may be different from the amount
+ * of the spare area we actually expose to MTD. For example, for for
+ * 2K+112, we only expose 64 spare bytes, but the hardware needs to know
+ * the real facts.
+ */
+
+ spas = this->physical_geometry.page_oob_size >> 1;
+
+ /*
+ * The number of command phases needed to read a page is directly
+ * dependent on whether this is a small page or large page device. Large
+ * page devices need more address phases, terminated by a second command
+ * phase.
+ */
+
+ cmd_phases = is_large_page_chip(this) ? 1 : 0;
+
+ /*
+ * The num_adr_phases1 field contains the number of phases needed to
+ * transmit addresses for read and program operations. This is the sum
+ * of the number of phases for a page address and the number of phases
+ * for a column address.
+ *
+ * The number of phases for a page address is the number of bytes needed
+ * to contain a page address.
+ *
+ * The number of phases for a column address is the number of bytes
+ * needed to contain a column address.
+ *
+ * After computing the sum, we subtract three because a value of zero in
+ * this field indicates three address phases, and this is the minimum
+ * number of phases the hardware can comprehend.
+ *
+ * We compute the number of phases based on the *physical* geometry, not
+ * the NFC geometry. For example, even if we are treating a very large
+ * device as if it contains fewer pages than it actually does, the
+ * hardware still needs the additional address phases.
+ */
+
+ pages_per_chip =
+ physical->chip_size >> (fls(physical->page_data_size) - 1);
+
+ addr_phases1 = (fls(pages_per_chip) >> 3) + 1;
+
+ addr_phases1 += (fls(physical->page_data_size) >> 3) + 1;
+
+ addr_phases1 -= 3;
+
+ /*
+ * The num_adr_phases0 field contains the number of phases needed to
+ * transmit a page address for an erase operation. That is, this is
+ * the value of addr_phases1, less the number of phases for the column
+ * address.
+ *
+ * The hardware expresses this phase count as one or two cycles less
+ * than the count indicated by add_phases1 (see the reference manual).
+ */
+
+ addr_phases0 = is_large_page_chip(this) ? 1 : 0;
+
+ /* Set the CONFIG2 register. */
+
+ mask =
+ NFC_3_2_CONFIG2_PS_MSK |
+ NFC_3_2_CONFIG2_CMD_PHASES_MSK |
+ NFC_3_2_CONFIG2_ADDR_PHASES0_MSK |
+ NFC_3_2_CONFIG2_ECC_MODE_MSK |
+ NFC_3_2_CONFIG2_PPB_MSK |
+ NFC_3_2_CONFIG2_ADDR_PHASES1_MSK |
+ NFC_3_2_CONFIG2_SPAS_MSK ;
+
+ config2 = __raw_readl(secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+ config2 &= ~mask;
+
+ config2 |= ps << NFC_3_2_CONFIG2_PS_POS;
+ config2 |= cmd_phases << NFC_3_2_CONFIG2_CMD_PHASES_POS;
+ config2 |= addr_phases0 << NFC_3_2_CONFIG2_ADDR_PHASES0_POS;
+ config2 |= ecc_mode << NFC_3_2_CONFIG2_ECC_MODE_POS;
+ config2 |= ppb << NFC_3_2_CONFIG2_PPB_POS;
+ config2 |= addr_phases1 << NFC_3_2_CONFIG2_ADDR_PHASES1_POS;
+ config2 |= spas << NFC_3_2_CONFIG2_SPAS_POS;
+
+ config2 = __raw_writel(config2,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+ /*
+ * Compute the num_of_devices field.
+ *
+ * It's very important to set this field correctly. This controls the
+ * set of ready/busy lines to which the NFC listens with automatic
+ * transactions. If this number is too large, the NFC will listen to
+ * ready/busy signals that are electrically floating, or it will try to
+ * read the status registers of chips that don't exist. Conversely, if
+ * the number is too small, the NFC could believe an operation is
+ * finished when some chips are still busy.
+ */
+
+ num_of_devices = physical->chip_count - 1;
+
+ /* Set the CONFIG3 register. */
+
+ mask = NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK;
+
+ config3 = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ config3 &= ~mask;
+
+ config3 |= num_of_devices << NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
+
+ __raw_writel(config3, secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ /*
+ * Check if the physical chip count is a power of 2. If not, then
+ * automatic operations aren't available. This is because we use an
+ * addressing option (see the ADD_OP field of CONFIG3) that requires
+ * a number of chips that is a power of 2.
+ */
+
+ if (ffs(physical->chip_count) != fls(physical->chip_count)) {
+ this->nfc->start_auto_read = 0;
+ this->nfc->start_auto_write = 0;
+ this->nfc->start_auto_erase = 0;
+ }
+
+ /* Unlock the NFC RAM. */
+
+ x = __raw_readl(secondary_base + NFC_3_2_WRPROT_REG_OFF);
+ x &= ~NFC_3_2_WRPROT_BLS_MSK;
+ x |= 0x2 << NFC_3_2_WRPROT_BLS_POS;
+ __raw_writel(x, secondary_base + NFC_3_2_WRPROT_REG_OFF);
+
+ /* Loop over chip selects, setting the unlocked ranges. */
+
+ for (chip = 0; chip < this->nfc->max_chip_count; chip++) {
+
+ /* Set the unlocked range to cover the entire chip.*/
+
+ __raw_writel(0xffff0000, secondary_base +
+ NFC_3_2_UNLOCK_BLK_ADD0_REG_OFF + (chip * 4));
+
+ /* Unlock. */
+
+ x = __raw_readl(secondary_base + NFC_3_2_WRPROT_REG_OFF);
+ x &= ~(NFC_3_2_WRPROT_CS2L_MSK | NFC_3_2_WRPROT_WPC_MSK);
+ x |= chip << NFC_3_2_WRPROT_CS2L_POS;
+ x |= 0x4 << NFC_3_2_WRPROT_WPC_POS ;
+ __raw_writel(x, secondary_base + NFC_3_2_WRPROT_REG_OFF);
+
+ }
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * nfc_3_2_exit() - Hardware cleanup.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_exit(struct imx_nfc_data *this)
+{
+}
+
+/**
+ * nfc_3_2_set_closest_cycle() - Version-specific hardware cleanup.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_set_closest_cycle(struct imx_nfc_data *this, unsigned ns)
+{
+ struct clk *parent_clock;
+ unsigned long parent_clock_rate_in_hz;
+ unsigned long sym_low_clock_rate_in_hz;
+ unsigned long asym_low_clock_rate_in_hz;
+ unsigned int sym_high_cycle_in_ns;
+ unsigned int asym_high_cycle_in_ns;
+
+ /*
+ * According to ENGcm09121:
+ *
+ * - If the NFC is set to SYMMETRIC mode, the NFC clock divider must
+ * divide the EMI Slow Clock by NO MORE THAN 4.
+ *
+ * - If the NFC is set for ASYMMETRIC mode, the NFC clock divider must
+ * divide the EMI Slow Clock by NO MORE THAN 3.
+ *
+ * We need to compute the corresponding cycle time constraints. Start
+ * by getting information about the parent clock.
+ */
+
+ parent_clock = clk_get_parent(this->clock);
+ parent_clock_rate_in_hz = clk_get_rate(parent_clock);
+
+ /* Compute the limit frequencies. */
+
+ sym_low_clock_rate_in_hz = parent_clock_rate_in_hz / 4;
+ asym_low_clock_rate_in_hz = parent_clock_rate_in_hz / 3;
+
+ /* Compute the corresponding limit cycle periods. */
+
+ sym_high_cycle_in_ns = 1000000000 / sym_low_clock_rate_in_hz;
+ asym_high_cycle_in_ns = (1000000000 / asym_low_clock_rate_in_hz) * 2;
+
+ /* Attempt to implement the given cycle. */
+
+ return nfc_util_set_best_cycle(this, ns,
+ ns > asym_high_cycle_in_ns, ns > sym_high_cycle_in_ns);
+
+}
+
+/**
+ * nfc_3_2_mask_interrupt() - Masks interrupts.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_mask_interrupt(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+ raw_set_mask_l(NFC_3_2_CONFIG2_INT_MSK_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+}
+
+/**
+ * nfc_3_2_unmask_interrupt() - Unmasks interrupts.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_unmask_interrupt(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+ raw_clr_mask_l(NFC_3_2_CONFIG2_INT_MSK_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+}
+
+/**
+ * nfc_3_2_clear_interrupt() - Clears an interrupt.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_clear_interrupt(struct imx_nfc_data *this)
+{
+ int done;
+ void *secondary_base = this->secondary_regs;
+
+ /* Request IP bus interface access. */
+
+ raw_set_mask_l(NFC_3_2_IPC_CREQ_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ /* Wait for access. */
+
+ do
+ done = !!raw_read_mask_l(NFC_3_2_IPC_CACK_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+ while (!done);
+
+ /* Clear the interrupt. */
+
+ raw_clr_mask_l(NFC_3_2_IPC_INT_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ /* Release the IP bus interface. */
+
+ raw_clr_mask_l(NFC_3_2_IPC_CREQ_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_is_interrupting() - Returns the interrupt bit status.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_is_interrupting(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+ return !!raw_read_mask_l(NFC_3_2_IPC_INT_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+}
+
+/**
+ * nfc_3_2_is_ready() - Returns the ready/busy status.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_is_ready(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+ return !!raw_read_mask_l(NFC_3_2_IPC_RB_B_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+}
+
+/**
+ * nfc_3_2_set_force_ce() - Can force CE to be asserted always.
+ *
+ * @this: Per-device data.
+ * @on: Indicates if the hardware CE signal should be asserted always.
+ */
+static void nfc_3_2_set_force_ce(struct imx_nfc_data *this, int on)
+{
+ void *primary_base = this->primary_regs;
+
+ if (on)
+ raw_set_mask_l(NFC_3_2_CONFIG1_NF_CE_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+ else
+ raw_clr_mask_l(NFC_3_2_CONFIG1_NF_CE_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_set_ecc() - Turns ECC on or off.
+ *
+ * @this: Per-device data.
+ * @on: Indicates if ECC should be on or off.
+ */
+static void nfc_3_2_set_ecc(struct imx_nfc_data *this, int on)
+{
+ void *secondary_base = this->secondary_regs;
+
+ if (on)
+ raw_set_mask_l(NFC_3_2_CONFIG2_ECC_EN_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+ else
+ raw_clr_mask_l(NFC_3_2_CONFIG2_ECC_EN_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_get_ecc_status() - Reports ECC errors.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_get_ecc_status(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ void *base = this->primary_regs;
+ uint16_t status_reg;
+ unsigned int buffer_status[8];
+ int status;
+
+ /* Get the entire status register. */
+
+ status_reg = __raw_readw(base + NFC_3_2_ECC_STATUS_REG_OFF);
+
+ /* Pick out the status for each buffer. */
+
+ buffer_status[0] = (status_reg & NFC_3_2_ECC_STATUS_NOBER1_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER1_POS;
+
+ buffer_status[1] = (status_reg & NFC_3_2_ECC_STATUS_NOBER2_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER2_POS;
+
+ buffer_status[2] = (status_reg & NFC_3_2_ECC_STATUS_NOBER3_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER3_POS;
+
+ buffer_status[3] = (status_reg & NFC_3_2_ECC_STATUS_NOBER4_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER4_POS;
+
+ buffer_status[4] = (status_reg & NFC_3_2_ECC_STATUS_NOBER5_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER5_POS;
+
+ buffer_status[5] = (status_reg & NFC_3_2_ECC_STATUS_NOBER6_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER6_POS;
+
+ buffer_status[6] = (status_reg & NFC_3_2_ECC_STATUS_NOBER7_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER7_POS;
+
+ buffer_status[7] = (status_reg & NFC_3_2_ECC_STATUS_NOBER8_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER8_POS;
+
+ /* Loop through the buffers we're actually using. */
+
+ status = 0;
+
+ for (i = 0; i < this->nfc_geometry->buffer_count; i++) {
+
+ if (buffer_status[i] > this->nfc_geometry->ecc_strength) {
+ status = -1;
+ break;
+ }
+
+ status += buffer_status[i];
+
+ }
+
+ /* Return the final result. */
+
+ return status;
+
+}
+
+/**
+ * nfc_3_2_get_symmetric() - Indicates if the clock is symmetric.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_get_symmetric(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+
+ return !!raw_read_mask_w(NFC_3_2_CONFIG2_SYM_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_set_symmetric() - Turns symmetric clock mode on or off.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_set_symmetric(struct imx_nfc_data *this, int on)
+{
+ void *secondary_base = this->secondary_regs;
+
+ if (on)
+ raw_set_mask_l(NFC_3_2_CONFIG2_SYM_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+ else
+ raw_clr_mask_l(NFC_3_2_CONFIG2_SYM_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_select_chip() - Selects the current chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip number to select, or -1 to select no chip.
+ */
+static void nfc_3_2_select_chip(struct imx_nfc_data *this, int chip)
+{
+ unsigned long x;
+ void *primary_base = this->primary_regs;
+
+ if (chip < 0)
+ return;
+
+ x = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ x &= ~NFC_3_2_CONFIG1_CS_MSK;
+
+ x |= (chip << NFC_3_2_CONFIG1_CS_POS) & NFC_3_2_CONFIG1_CS_MSK;
+
+ __raw_writel(x, primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_command_cycle() - Sends a command.
+ *
+ * @this: Per-device data.
+ * @command: The command code.
+ */
+static void nfc_3_2_command_cycle(struct imx_nfc_data *this, unsigned command)
+{
+ void *primary_base = this->primary_regs;
+
+ /* Write the command we want to send. */
+
+ __raw_writel(command, primary_base + NFC_3_2_CMD_REG_OFF);
+
+ /* Launch a command cycle. */
+
+ __raw_writel(NFC_3_2_LAUNCH_FCMD_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_write_cycle() - writes a single byte.
+ *
+ * @this: Per-device data.
+ * @byte: The byte.
+ */
+static void nfc_3_2_write_cycle(struct imx_nfc_data *this, unsigned int byte)
+{
+ void *primary_base = this->primary_regs;
+
+ /* Give the NFC the byte we want to write. */
+
+ __raw_writel(byte, primary_base + NFC_3_2_ADD0_REG_OFF);
+
+ /* Launch an address cycle.
+ *
+ * This is *sort* of a hack, but not really. The intent of the NFC
+ * design is for this operation to send an address byte. In fact, the
+ * NFC neither knows nor cares what we're sending. It justs runs a write
+ * cycle.
+ */
+
+ __raw_writel(NFC_3_2_LAUNCH_FADD_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+}
+
+/**
+ * nfc_3_2_read_cycle() - Applies a single read cycle to the current chip.
+ *
+ * @this: Per-device data.
+ */
+static unsigned int nfc_3_2_read_cycle(struct imx_nfc_data *this)
+{
+ unsigned int result;
+ void *primary_base = this->primary_regs;
+
+ /* Launch a "Data Out" operation. */
+
+ __raw_writel(0x4 << NFC_3_2_LAUNCH_FDO_POS,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+ /* Get the result. */
+
+ result = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF)
+ >> NFC_3_2_CONFIG1_STATUS_POS;
+ result &= 0xff;
+
+ /* Return the result. */
+
+ return result;
+
+}
+
+/**
+ * nfc_3_2_read_page() - Reads a page into the NFC memory.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_read_page(struct imx_nfc_data *this)
+{
+ void *primary_base = this->primary_regs;
+
+ /* Start reading into buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Launch a page data out operation. */
+
+ __raw_writel(0x1 << NFC_3_2_LAUNCH_FDO_POS,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+}
+
+/**
+ * nfc_3_2_send_page() - Sends a page from the NFC to the current chip.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_send_page(struct imx_nfc_data *this)
+{
+ void *primary_base = this->primary_regs;
+
+ /* Start sending from buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Launch a page data in operation. */
+
+ __raw_writel(NFC_3_2_LAUNCH_FDI_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+}
+
+/**
+ * nfc_3_2_add_state_events() - Adds events to display important state.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_add_state_events(struct imx_nfc_data *this)
+{
+#ifdef EVENT_REPORTING
+ void *secondary_base = this->secondary_regs;
+
+ add_state_event_l
+ (
+ secondary_base + NFC_3_2_IPC_REG_OFF,
+ NFC_3_2_IPC_INT_MSK,
+ " Interrupt : 0",
+ " Interrupt : X"
+ );
+
+ add_state_event_l
+ (
+ secondary_base + NFC_3_2_IPC_REG_OFF,
+ NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
+ " auto_prog_done: 0",
+ " auto_prog_done: X"
+ );
+
+ add_state_event_l
+ (
+ secondary_base + NFC_3_2_IPC_REG_OFF,
+ NFC_3_2_IPC_RB_B_MSK,
+ " Medium : Busy",
+ " Medium : Ready"
+ );
+#endif
+}
+
+/**
+ * nfc_3_2_get_auto_loop_params() - Gets automatic operation loop parameters.
+ *
+ * This function and the corresponding "setter" enable the automatic operations
+ * to keep some state as they iterate over chips.
+ *
+ * The most "obvious" way to save state would be to allocate a private data
+ * structure and hang it off the owning struct nfc_hal. On the other hand,
+ * writing the code to allocate the memory and then release it when the NFC
+ * shuts down is annoying - and we have some perfectly good memory in the NFC
+ * hardware that we can use. Since we only use two commands at a time, we can
+ * stash our loop limits and loop index in the top 16 bits of the NAND_CMD
+ * register. To paraphrase the reference manual:
+ *
+ *
+ * NAND_CMD
+ *
+ * |<-- 4 bits -->|<-- 4 bits -->|<-- 8 bits -->|
+ * +----------------+---------------+--------------------------------+
+ * | First | Last | Loop Index |
+ * +----------------+---------------+--------------------------------+
+ * | NAND COMMAND1 | NAND COMMAND0 |
+ * +--------------------------------+--------------------------------+
+ * |<-- 16 bits -->|<-- 16 bits -->|
+ *
+ *
+ * @this: Per-device data.
+ * @first: A pointer to a variable that will receive the first chip number.
+ * @last: A pointer to a variable that will receive the last chip number.
+ * @index: A pointer to a variable that will receive the current chip number.
+ */
+static void nfc_3_2_get_auto_loop_params(struct imx_nfc_data *this,
+ unsigned *first, unsigned *last, unsigned *index)
+{
+ uint32_t x;
+ void *primary_base = this->primary_regs;
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ *first = (x >> 28) & 0x0f;
+ *last = (x >> 24) & 0x0f;
+ *index = (x >> 16) & 0xff;
+
+}
+
+/**
+ * nfc_3_2_set_auto_loop_params() - Sets automatic operation loop parameters.
+ *
+ * See nfc_3_2_get_auto_loop_params() for detailed information about these
+ * functions.
+ *
+ * @this: Per-device data.
+ * @first: The first chip number.
+ * @last: The last chip number.
+ * @index: The current chip number.
+ */
+static void nfc_3_2_set_auto_loop_params(struct imx_nfc_data *this,
+ unsigned first, unsigned last, unsigned index)
+{
+ uint32_t x;
+ void *primary_base = this->primary_regs;
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ x &= 0x0000ffff;
+ x |= (first & 0x0f) << 28;
+ x |= (last & 0x0f) << 24;
+ x |= (index & 0xff) << 16;
+
+ __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_get_auto_addresses() - Gets automatic operation addresses.
+ *
+ * @this: Per-device data.
+ * @group: The address group number.
+ * @chip: A pointer to a variable that will receive the chip number.
+ * @column: A pointer to a variable that will receive the column address.
+ * A NULL pointer indicates there is no column address.
+ * @page: A pointer to a variable that will receive the page address.
+ */
+static void nfc_3_2_get_auto_addresses(struct imx_nfc_data *this,
+ unsigned group, unsigned *chip, unsigned *column, unsigned *page)
+{
+ uint32_t x;
+ unsigned int chip_count;
+ unsigned int cs_width;
+ unsigned int cs_mask;
+ unsigned int page_lsbs;
+ unsigned int page_msbs;
+ uint32_t *low;
+ uint16_t *high;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ /*
+ * The width of the chip select field depends on the number of connected
+ * chips.
+ *
+ * Notice that these computations work only if the number of chips is a
+ * power of 2. In fact, that is a fundamental limitation for using
+ * automatic operations.
+ */
+
+ x = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ chip_count =
+ (x & NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK) >>
+ NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
+ chip_count++;
+
+ cs_width = ffs(chip_count) - 1;
+ cs_mask = chip_count - 1;
+
+ /* Construct pointers to the pieces of the given address group. */
+
+ low = primary_base + NFC_3_2_ADD0_REG_OFF;
+ low += group;
+
+ high = primary_base + NFC_3_2_ADD8_REG_OFF;
+ high += group;
+
+ /* Check if there's a column address. */
+
+ if (column) {
+
+ /*
+ * The low 32 bits of the address group look like this:
+ *
+ * 16 - n n
+ * | <- bits ->|<->|<- 16 bits ->|
+ * +-------------+---+----------------+
+ * | Page LSBs |CS | Column |
+ * +-------------+---+----------------+
+ */
+
+ x = __raw_readl(low);
+
+ *column = x & 0xffff;
+ *chip = (x >> 16) & cs_mask;
+ page_lsbs = x >> (16 + cs_width);
+
+ /* The high 16 bits contain the MSB's of the page address. */
+
+ page_msbs = __raw_readw(high);
+
+ *page = (page_msbs << (16 - cs_width)) | page_lsbs;
+
+ } else {
+
+ /*
+ * The low 32 bits of the address group look like this:
+ *
+ * n
+ * | <- (32 - n) bits ->|<->|
+ * +-----------------------------+---+
+ * | Page LSBs |CS |
+ * +-----------------------------+---+
+ */
+
+ x = __raw_readl(low);
+
+ *chip = x & cs_mask;
+ page_lsbs = x >> cs_width;
+
+ /* The high 16 bits contain the MSB's of the page address. */
+
+ page_msbs = __raw_readw(high);
+
+ *page = (page_msbs << (32 - cs_width)) | page_lsbs;
+
+ }
+
+}
+
+/**
+ * nfc_3_2_set_auto_addresses() - Sets automatic operation addresses.
+ *
+ * @this: Per-device data.
+ * @group: The address group number.
+ * @chip: The chip number.
+ * @column: The column address. The sentinel value ~0 indicates that there is
+ * no column address.
+ * @page: The page address.
+ */
+static void nfc_3_2_set_auto_addresses(struct imx_nfc_data *this,
+ unsigned group, unsigned chip, unsigned column, unsigned page)
+{
+ uint32_t x;
+ unsigned chip_count;
+ unsigned int cs_width;
+ unsigned int cs_mask;
+ uint32_t *low;
+ uint16_t *high;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ /*
+ * The width of the chip select field depends on the number of connected
+ * chips.
+ *
+ * Notice that these computations work only if the number of chips is a
+ * power of 2. In fact, that is a fundamental limitation for using
+ * automatic operations.
+ */
+
+ x = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ chip_count =
+ (x & NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK) >>
+ NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
+ chip_count++;
+
+ cs_width = ffs(chip_count) - 1;
+ cs_mask = chip_count - 1;
+
+ /* Construct pointers to the pieces of the given address group. */
+
+ low = primary_base + NFC_3_2_ADD0_REG_OFF;
+ low += group;
+
+ high = primary_base + NFC_3_2_ADD8_REG_OFF;
+ high += group;
+
+ /* Check if we have a column address. */
+
+ if (column != ~0) {
+
+ /*
+ * The low 32 bits of the address group look like this:
+ *
+ * 16 - n n
+ * | <- bits ->|<->|<- 16 bits ->|
+ * +-------------+---+----------------+
+ * | Page LSBs |CS | Column |
+ * +-------------+---+----------------+
+ */
+
+ x = 0;
+ x |= column & 0xffff;
+ x |= (chip & cs_mask) << 16;
+ x |= page << (16 + cs_width);
+
+ __raw_writel(x, low);
+
+ /* The high 16 bits contain the MSB's of the page address. */
+
+ x = (page >> (16 - cs_width)) & 0xffff;
+
+ __raw_writew(x, high);
+
+ } else {
+
+ /*
+ * The low 32 bits of the address group look like this:
+ *
+ * n
+ * | <- (32 - n) bits ->|<->|
+ * +-----------------------------+---+
+ * | Page LSBs |CS |
+ * +-----------------------------+---+
+ */
+
+ x = 0;
+ x |= chip & cs_mask;
+ x |= page << cs_width;
+
+ __raw_writel(x, low);
+
+ /* The high 16 bits contain the MSB's of the page address. */
+
+ x = (page >> (32 - cs_width)) & 0xffff;
+
+ __raw_writew(x, high);
+
+ }
+
+}
+
+/**
+ * nfc_3_2_start_auto_read() - Starts an automatic read.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ * @start: The first physical chip number on which to operate.
+ * @count: The number of physical chips on which to operate.
+ * @column: The column address.
+ * @page: The page address.
+ */
+static int nfc_3_2_start_auto_read(struct imx_nfc_data *this,
+ unsigned start, unsigned count, unsigned column, unsigned page)
+{
+ uint32_t x;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+
+ add_event("Entering nfc_3_2_start_auto_read", 1);
+
+ /* Check for nonsense. */
+
+ if ((start > 7) || (!count) || (count > 8)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Set state. */
+
+ nfc_3_2_set_auto_loop_params(this, start, start + count - 1, start);
+ nfc_3_2_set_auto_addresses(this, 0, start, column, page);
+
+ /* Set up for ONE iteration at a time. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_ITER_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Reset to buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /*
+ * Set up the commands. Note that the number of command phases was
+ * configured in the set_geometry() function so, even though we're
+ * giving both commands here, they won't necessarily both be used.
+ */
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ x &= 0xffff0000;
+ x |= NAND_CMD_READ0 << 0;
+ x |= NAND_CMD_READSTART << 8;
+
+ __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);
+
+ /* Launch the operation. */
+
+ add_event("Launching", 0);
+
+ __raw_writel(NFC_3_2_LAUNCH_AUTO_READ_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_start_auto_read", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_wait_for_auto_read() - Waits until auto read is ready for the CPU.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_wait_for_auto_read(struct imx_nfc_data *this)
+{
+ unsigned int first;
+ unsigned int last;
+ unsigned int index;
+ int return_value = 0;
+
+ add_event("Entering nfc_3_2_wait_for_auto_read", 1);
+
+ /* Get state. */
+
+ nfc_3_2_get_auto_loop_params(this, &first, &last, &index);
+
+ /* This function should be called for every chip. */
+
+ if ((index < first) || (index > last)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Wait for the NFC to completely finish and interrupt. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_wait_for_auto_read", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_resume_auto_read() - Resumes auto read after CPU intervention.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_resume_auto_read(struct imx_nfc_data *this)
+{
+ unsigned int first;
+ unsigned int last;
+ unsigned int index;
+ unsigned int chip;
+ unsigned int column;
+ unsigned int page;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+
+ add_event("Entering nfc_3_2_resume_auto_read", 1);
+
+ /* Get state. */
+
+ nfc_3_2_get_auto_loop_params(this, &first, &last, &index);
+ nfc_3_2_get_auto_addresses(this, 0, &chip, &column, &page);
+
+ /* This function should be called for every chip, except the last. */
+
+ if ((index < first) || (index >= last)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Move to the next chip. */
+
+ index++;
+
+ /* Update state. */
+
+ nfc_3_2_set_auto_loop_params(this, first, last, index);
+ nfc_3_2_set_auto_addresses(this, 0, index, column, page);
+
+ /* Reset to buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Launch the operation. */
+
+ add_event("Launching", 0);
+
+ __raw_writel(NFC_3_2_LAUNCH_AUTO_READ_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_resume_auto_read", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_start_auto_write() - Starts an automatic write.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ * @start: The first physical chip number on which to operate.
+ * @count: The number of physical chips on which to operate.
+ * @column: The column address.
+ * @page: The page address.
+ */
+static int nfc_3_2_start_auto_write(struct imx_nfc_data *this,
+ unsigned start, unsigned count, unsigned column, unsigned page)
+{
+ uint32_t x;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ add_event("Entering nfc_3_2_start_auto_write", 1);
+
+ /* Check for nonsense. */
+
+ if ((start > 7) || (!count) || (count > 8)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Set state. */
+
+ nfc_3_2_set_auto_loop_params(this, start, start + count - 1, start);
+ nfc_3_2_set_auto_addresses(this, 0, start, column, page);
+
+ /* Set up for ONE iteration at a time. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_ITER_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Set up the commands. */
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ x &= 0xffff0000;
+ x |= NAND_CMD_SEQIN << 0;
+ x |= NAND_CMD_PAGEPROG << 8;
+
+ __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);
+
+ /* Clear the auto_prog_done bit. */
+
+ raw_clr_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_start_auto_write", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_wait_for_auto_write() - Waits for auto write to be writey for the CPU.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_wait_for_auto_write(struct imx_nfc_data *this)
+{
+ unsigned int first;
+ unsigned int last;
+ unsigned int index;
+ unsigned int chip;
+ unsigned int column;
+ unsigned int page;
+ uint32_t x;
+ int interrupt;
+ int transmitted;
+ int ready;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ add_event("Entering nfc_3_2_wait_for_auto_write", 1);
+
+ /* Get state. */
+
+ nfc_3_2_get_auto_loop_params(this, &first, &last, &index);
+ nfc_3_2_get_auto_addresses(this, 0, &chip, &column, &page);
+
+ /* This function should be called for every chip. */
+
+ if ((index < first) || (index > last)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Reset to buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Launch the operation. */
+
+ nfc_3_2_add_state_events(this);
+
+ add_event("Launching", 0);
+
+ __raw_writel(NFC_3_2_LAUNCH_AUTO_PROG_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ nfc_3_2_add_state_events(this);
+
+ /* Wait for the NFC to transmit the page. */
+
+ add_event("Spinning while the NFC transmits the page...", 0);
+
+ do
+ transmitted = !!raw_read_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+ while (!transmitted);
+
+ /*
+ * When control arrives here, the auto_prog_done bit is set. This
+ * indicates the NFC has finished transmitting the current page. The CPU
+ * is now free to write the next page into the NFC's memory. The Flash
+ * hardware is still busy programming the page into its storage array.
+ *
+ * Clear the auto_prog_done bit. This is analogous to acknowledging an
+ * interrupt.
+ */
+
+ nfc_3_2_add_state_events(this);
+
+ add_event("Acknowledging the page...", 0);
+
+ raw_clr_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ nfc_3_2_add_state_events(this);
+
+ /*
+ * If this is *not* the last iteration, move to the next chip and return
+ * to the caller so he can put the next page in the NFC buffer.
+ */
+
+ if (index < last) {
+
+ add_event("Moving to the next chip...", 0);
+
+ index++;
+
+ nfc_3_2_set_auto_loop_params(this, first, last, index);
+ nfc_3_2_set_auto_addresses(this, 0, index, column, page);
+
+ goto exit;
+
+ }
+
+ /*
+ * If control arrives here, this is the last iteration, so it's time to
+ * close out the entire operation. We need to wait for the medium to be
+ * ready and then acknowledge the final interrupt.
+ *
+ * Because of the way the NFC hardware works, the code here requires a
+ * bit of explanation. The most important rule is:
+ *
+ * During automatic operations, the NFC sets its
+ * interrupt bit *whenever* it sees the ready/busy
+ * signal transition from "Busy" to "Ready".
+ *
+ * Recall that the ready/busy signals from all the chips in the medium
+ * are "wire-anded." Thus, the NFC will only see that the medium is
+ * ready if *all* chips are ready.
+ *
+ * Because of variability in NAND Flash timing, the medium *may* have
+ * become ready during previous iterations, which means the interrupt
+ * bit *may* be set at this moment. This is a "left-over" interrupt, and
+ * can complicate our logic.
+ *
+ * The two bits of state that interest us here are the interrupt bit
+ * and the ready/busy bit. It boils down to the following truth table:
+ *
+ * | Interrupt | Ready/Busy | Description
+ * +------------+------------+---------------
+ * | | | Busy medium and no left-over interrupt.
+ * | 0 | 0 | The final interrupt will arrive in the
+ * | | | future.
+ * +------------+------------+---------------
+ * | | | Ready medium and no left-over interrupt.
+ * | 0 | 1 | There will be no final interrupt. This
+ * | | | case should be impossible.
+ * +------------+------------+---------------
+ * | | | Busy medium and left-over interrupt.
+ * | 1 | 0 | The final interrupt will arrive in the
+ * | | | future. This is the hard case.
+ * +------------+------------+---------------
+ * | | | Ready medium and left-over interrupt.
+ * | 1 | 1 | The final interrupt has already
+ * | | | arrived. Acknowledge it and exit.
+ * +------------+------------+---------------
+ *
+ * Case #3 is a small problem. If we clear the interrupt, we may or may
+ * not have another interrupt following.
+ */
+
+ /* Sample the IPC register. */
+
+ x = __raw_readl(secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ interrupt = !!(x & NFC_3_2_IPC_INT_MSK);
+ ready = !!(x & NFC_3_2_IPC_RB_B_MSK);
+
+ /* Check for the easy cases. */
+
+ if (!interrupt && !ready) {
+ add_event("Waiting for the final interrupt..." , 0);
+ nfc_util_wait_for_the_nfc(this, true);
+ goto exit;
+ } else if (!interrupt && ready) {
+ add_event("Done." , 0);
+ goto exit;
+ } else if (interrupt && ready) {
+ add_event("Acknowledging the final interrupt..." , 0);
+ nfc_util_wait_for_the_nfc(this, false);
+ goto exit;
+
+ }
+
+ /*
+ * If control arrives here, we hit case #3. Begin by acknowledging the
+ * interrupt we have right now.
+ */
+
+ add_event("Clearing the left-over interrupt..." , 0);
+ nfc_util_wait_for_the_nfc(this, false);
+
+ /*
+ * Check the ready/busy bit again. If the medium is still busy, then
+ * we're going to get one more interrupt.
+ */
+
+ ready = !!raw_read_mask_l(NFC_3_2_IPC_RB_B_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ if (!ready) {
+ add_event("Waiting for the final interrupt..." , 0);
+ nfc_util_wait_for_the_nfc(this, true);
+ }
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_wait_for_auto_write", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_start_auto_erase() - Starts an automatic erase.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ * @start: The first physical chip number on which to operate.
+ * @count: The number of physical chips on which to operate.
+ * @page: The page address.
+ */
+static int nfc_3_2_start_auto_erase(struct imx_nfc_data *this,
+ unsigned start, unsigned count, unsigned page)
+{
+ uint32_t x;
+ unsigned i;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+
+ add_event("Entering nfc_3_2_start_auto_erase", 1);
+
+ /* Check for nonsense. */
+
+ if ((start > 7) || (!count) || (count > 8)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Set up the commands. */
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ x &= 0xffff0000;
+ x |= NAND_CMD_ERASE1 << 0;
+ x |= NAND_CMD_ERASE2 << 8;
+
+ __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);
+
+ /* Set the iterations. */
+
+ x = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ x &= ~NFC_3_2_CONFIG1_ITER_MSK;
+ x |= ((count - 1) << NFC_3_2_CONFIG1_ITER_POS) &
+ NFC_3_2_CONFIG1_ITER_MSK;
+
+ __raw_writel(x, primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Loop over chips, setting up the address groups. */
+
+ for (i = 0; i < count; i++)
+ nfc_3_2_set_auto_addresses(this, i, start + i, ~0, page);
+
+ /* Launch the operation. */
+
+ add_event("Launching", 0);
+
+ __raw_writel(NFC_3_2_LAUNCH_AUTO_ERASE_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_start_auto_erase", -1);
+
+ return return_value;
+
+}
+
+/*
+ * At this point, we've defined all the version-specific primitives. We're now
+ * ready to construct the NFC HAL structures for every version.
+ */
+
+struct nfc_hal nfc_1_0_hal = {
+ .major_version = 1,
+ .minor_version = 0,
+ .max_chip_count = 1,
+ .max_buffer_count = 4,
+ .spare_buf_stride = 16,
+ .has_secondary_regs = 0,
+ .can_be_symmetric = 0,
+ };
+
+struct nfc_hal nfc_2_0_hal = {
+ .major_version = 2,
+ .minor_version = 0,
+ .max_chip_count = 1,
+ .max_buffer_count = 4,
+ .spare_buf_stride = 16,
+ .has_secondary_regs = false,
+ .can_be_symmetric = true,
+ .init = nfc_2_0_init,
+ .set_geometry = nfc_2_0_set_geometry,
+ .exit = nfc_2_x_exit,
+ .mask_interrupt = nfc_2_0_mask_interrupt,
+ .unmask_interrupt = nfc_2_0_unmask_interrupt,
+ .clear_interrupt = nfc_2_x_clear_interrupt,
+ .is_interrupting = nfc_2_x_is_interrupting,
+ .is_ready = 0, /* Ready/Busy not exposed. */
+ .set_ecc = nfc_2_0_set_ecc,
+ .get_ecc_status = nfc_2_0_get_ecc_status,
+ .get_symmetric = nfc_2_0_get_symmetric,
+ .set_symmetric = nfc_2_0_set_symmetric,
+ .select_chip = nfc_2_0_select_chip,
+ .command_cycle = nfc_2_x_command_cycle,
+ .write_cycle = nfc_2_x_write_cycle,
+ .read_cycle = nfc_2_0_read_cycle,
+ .read_page = nfc_2_0_read_page,
+ .send_page = nfc_2_0_send_page,
+ .start_auto_read = 0, /* Not supported. */
+ .wait_for_auto_read = 0, /* Not supported. */
+ .resume_auto_read = 0, /* Not supported. */
+ .start_auto_write = 0, /* Not supported. */
+ .wait_for_auto_write = 0, /* Not supported. */
+ .start_auto_erase = 0, /* Not supported. */
+ };
+
+struct nfc_hal nfc_2_1_hal = {
+ .major_version = 2,
+ .minor_version = 1,
+ .max_chip_count = 4,
+ .max_buffer_count = 8,
+ .spare_buf_stride = 64,
+ .has_secondary_regs = 0,
+ .can_be_symmetric = !0,
+ };
+
+struct nfc_hal nfc_3_1_hal = {
+ .major_version = 3,
+ .minor_version = 1,
+ .max_chip_count = 4,
+ .max_buffer_count = 8,
+ .spare_buf_stride = 64,
+ .has_secondary_regs = !0,
+ .can_be_symmetric = !0,
+ };
+
+struct nfc_hal nfc_3_2_hal = {
+ .major_version = 3,
+ .minor_version = 2,
+ .max_chip_count = 8,
+ .max_buffer_count = 8,
+ .spare_buf_stride = 64,
+ .has_secondary_regs = true,
+ .can_be_symmetric = true,
+ .init = nfc_3_2_init,
+ .set_geometry = nfc_3_2_set_geometry,
+ .exit = nfc_3_2_exit,
+ .set_closest_cycle = nfc_3_2_set_closest_cycle,
+ .mask_interrupt = nfc_3_2_mask_interrupt,
+ .unmask_interrupt = nfc_3_2_unmask_interrupt,
+ .clear_interrupt = nfc_3_2_clear_interrupt,
+ .is_interrupting = nfc_3_2_is_interrupting,
+ .is_ready = nfc_3_2_is_ready,
+ .set_force_ce = nfc_3_2_set_force_ce,
+ .set_ecc = nfc_3_2_set_ecc,
+ .get_ecc_status = nfc_3_2_get_ecc_status,
+ .get_symmetric = nfc_3_2_get_symmetric,
+ .set_symmetric = nfc_3_2_set_symmetric,
+ .select_chip = nfc_3_2_select_chip,
+ .command_cycle = nfc_3_2_command_cycle,
+ .write_cycle = nfc_3_2_write_cycle,
+ .read_cycle = nfc_3_2_read_cycle,
+ .read_page = nfc_3_2_read_page,
+ .send_page = nfc_3_2_send_page,
+ .start_auto_read = nfc_3_2_start_auto_read,
+ .wait_for_auto_read = nfc_3_2_wait_for_auto_read,
+ .resume_auto_read = nfc_3_2_resume_auto_read,
+ .start_auto_write = nfc_3_2_start_auto_write,
+ .wait_for_auto_write = nfc_3_2_wait_for_auto_write,
+ .start_auto_erase = nfc_3_2_start_auto_erase,
+ };
+
+/*
+ * This array has a pointer to every NFC HAL structure. The probing process will
+ * find the one that matches the version given by the platform.
+ */
+
+struct nfc_hal *(nfc_hals[]) = {
+ &nfc_1_0_hal,
+ &nfc_2_0_hal,
+ &nfc_2_1_hal,
+ &nfc_3_1_hal,
+ &nfc_3_2_hal,
+};
+
+/**
+ * mal_init() - Initialize the Medium Abstraction Layer.
+ *
+ * @this: Per-device data.
+ */
+static void mal_init(struct imx_nfc_data *this)
+{
+ this->interrupt_override = DRIVER_CHOICE;
+ this->auto_op_override = DRIVER_CHOICE;
+ this->inject_ecc_error = 0;
+}
+
+/**
+ * mal_set_physical_geometry() - Set up the physical medium geometry.
+ *
+ * This function retrieves the physical geometry information discovered by
+ * nand_scan(), corrects it, and records it in the per-device data structure.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_physical_geometry(struct imx_nfc_data *this)
+{
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *nand = &this->nand;
+ struct device *dev = this->dev;
+ uint8_t manufacturer_id;
+ uint8_t device_id;
+ unsigned int block_size_in_pages;
+ unsigned int chip_size_in_blocks;
+ unsigned int chip_size_in_pages;
+ uint64_t medium_size_in_bytes;
+ struct physical_geometry *physical = &this->physical_geometry;
+
+ /*
+ * Begin by transcribing exactly what the MTD code discovered. If there
+ * are any mistakes, we'll fix them in a moment.
+ */
+
+ physical->chip_count = nand->numchips;
+ physical->chip_size = nand->chipsize;
+ physical->block_size = mtd->erasesize;
+ physical->page_data_size = mtd->writesize;
+ physical->page_oob_size = mtd->oobsize;
+
+ /* Read some of the ID bytes from the first NAND Flash chip. */
+
+ nand->select_chip(mtd, 0);
+
+ nfc_util_send_cmd_and_addrs(this, NAND_CMD_READID, 0x00, -1);
+
+ manufacturer_id = nand->read_byte(mtd);
+ device_id = nand->read_byte(mtd);
+
+ /*
+ * Most manufacturers sell 4K page devices with 218 out-of-band bytes
+ * per page to accomodate ECC-8.
+ *
+ * Samsung and Hynix claim their parts have better reliability, so they
+ * only need ECC-4 and they have only 128 out-of-band bytes.
+ *
+ * The MTD code pays no attention to the manufacturer ID (something that
+ * eventually will have to change), so it believes that all 4K pages
+ * have 218 out-of-band bytes.
+ *
+ * We correct that mistake here.
+ */
+
+ if (physical->page_data_size == 4096) {
+ if ((manufacturer_id == NAND_MFR_SAMSUNG) ||
+ (manufacturer_id == NAND_MFR_HYNIX)) {
+ physical->page_oob_size = 128;
+ }
+ }
+
+ /* Compute some interesting facts. */
+
+ block_size_in_pages =
+ physical->block_size / physical->page_data_size;
+ chip_size_in_pages =
+ physical->chip_size >> (fls(physical->page_data_size) - 1);
+ chip_size_in_blocks =
+ physical->chip_size >> (fls(physical->block_size) - 1);
+ medium_size_in_bytes =
+ physical->chip_size * physical->chip_count;
+
+ /* Report. */
+
+ dev_dbg(dev, "-----------------\n");
+ dev_dbg(dev, "Physical Geometry\n");
+ dev_dbg(dev, "-----------------\n");
+ dev_dbg(dev, "Chip Count : %d\n", physical->chip_count);
+ dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
+ physical->page_data_size, physical->page_data_size);
+ dev_dbg(dev, "Page OOB Size in Bytes : %u\n",
+ physical->page_oob_size);
+ dev_dbg(dev, "Block Size in Bytes : %u (0x%x)\n",
+ physical->block_size, physical->block_size);
+ dev_dbg(dev, "Block Size in Pages : %u (0x%x)\n",
+ block_size_in_pages, block_size_in_pages);
+ dev_dbg(dev, "Chip Size in Bytes : %llu (0x%llx)\n",
+ physical->chip_size, physical->chip_size);
+ dev_dbg(dev, "Chip Size in Pages : %u (0x%x)\n",
+ chip_size_in_pages, chip_size_in_pages);
+ dev_dbg(dev, "Chip Size in Blocks : %u (0x%x)\n",
+ chip_size_in_blocks, chip_size_in_blocks);
+ dev_dbg(dev, "Medium Size in Bytes : %llu (0x%llx)\n",
+ medium_size_in_bytes, medium_size_in_bytes);
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * mal_set_nfc_geometry() - Set up the NFC geometry.
+ *
+ * This function calls the NFC HAL to select an NFC geometry that is compatible
+ * with the medium's physical geometry.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_nfc_geometry(struct imx_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct nfc_geometry *nfc;
+
+ /* Set the NFC geometry. */
+
+ if (this->nfc->set_geometry(this))
+ return !0;
+
+ /* Get a pointer to the new NFC geometry information. */
+
+ nfc = this->nfc_geometry;
+
+ /* Report. */
+
+ dev_dbg(dev, "------------\n");
+ dev_dbg(dev, "NFC Geometry\n");
+ dev_dbg(dev, "------------\n");
+ dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
+ nfc->page_data_size, nfc->page_data_size);
+ dev_dbg(dev, "Page OOB Size in Bytes : %u\n", nfc->page_oob_size);
+ dev_dbg(dev, "ECC Algorithm : %s\n", nfc->ecc_algorithm);
+ dev_dbg(dev, "ECC Strength : %d\n", nfc->ecc_strength);
+ dev_dbg(dev, "Buffer Count : %u\n", nfc->buffer_count);
+ dev_dbg(dev, "Spare Buffer Size : %u\n", nfc->spare_buf_size);
+ dev_dbg(dev, "Spare Buffer Spillover : %u\n", nfc->spare_buf_spill);
+ dev_dbg(dev, "Auto Read Available : %s\n",
+ this->nfc->start_auto_read ? "Yes" : "No");
+ dev_dbg(dev, "Auto Write Available : %s\n",
+ this->nfc->start_auto_write ? "Yes" : "No");
+ dev_dbg(dev, "Auto Erase Available : %s\n",
+ this->nfc->start_auto_erase ? "Yes" : "No");
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * mal_set_logical_geometry() - Set up the logical medium geometry.
+ *
+ * This function constructs the logical geometry that we will expose to MTD,
+ * based on the physical and NFC geometries, and whether or not interleaving is
+ * on.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_logical_geometry(struct imx_nfc_data *this)
+{
+ const uint32_t max_medium_size_in_bytes = ~0;
+ int we_are_interleaving;
+ uint64_t physical_medium_size_in_bytes;
+ unsigned int usable_blocks;
+ unsigned int block_size_in_pages;
+ unsigned int chip_size_in_blocks;
+ unsigned int chip_size_in_pages;
+ unsigned int usable_medium_size_in_pages;
+ unsigned int usable_medium_size_in_blocks;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct nfc_geometry *nfc = this->nfc_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+ struct device *dev = this->dev;
+
+ /* Figure out if we're interleaving. */
+
+ we_are_interleaving = this->pdata->interleave;
+
+ switch (imx_nfc_module_interleave_override) {
+
+ case NEVER:
+ we_are_interleaving = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ we_are_interleaving = true;
+ break;
+
+ }
+
+ /* Compute the physical size of the medium. */
+
+ physical_medium_size_in_bytes =
+ physical->chip_count * physical->chip_size;
+
+ /* Compute the logical geometry. */
+
+ if (!we_are_interleaving) {
+
+ /*
+ * At this writing, MTD uses unsigned 32-bit variables to
+ * represent the size of the medium. If the physical medium is
+ * larger than that, the logical medium must be smaller. Here,
+ * we compute the total number of physical blocks in the medium
+ * that we can actually use.
+ */
+
+ if (physical_medium_size_in_bytes <= max_medium_size_in_bytes) {
+ usable_blocks =
+ physical_medium_size_in_bytes >>
+ (ffs(physical->block_size) - 1);
+ } else {
+ usable_blocks =
+ max_medium_size_in_bytes / physical->block_size;
+ }
+
+ /* Set up the logical geometry.
+ *
+ * Notice that the usable medium size is not necessarily the
+ * same as the chip size multiplied by the number of physical
+ * chips. We can't afford to touch the physical chip size
+ * because the NAND Flash MTD code *requires* it to be a power
+ * of 2.
+ */
+
+ logical->chip_count = physical->chip_count;
+ logical->chip_size = physical->chip_size;
+ logical->usable_size = usable_blocks * physical->block_size;
+ logical->block_size = physical->block_size;
+ logical->page_data_size = nfc->page_data_size;
+
+ /* Use the MTD layout that best matches the NFC geometry. */
+
+ logical->mtd_layout = &nfc->mtd_layout;
+ logical->page_oob_size = nfc->mtd_layout.eccbytes +
+ nfc->mtd_layout.oobavail;
+
+ } else {
+
+ /*
+ * If control arrives here, we are interleaving. Specifically,
+ * we are "horizontally concatenating" the pages in all the
+ * physical chips.
+ *
+ * - A logical page will be the size of a physical page
+ * multiplied by the number of physical chips.
+ *
+ * - A logical block will have the same number of pages as a
+ * physical block but, since the logical page size is larger,
+ * the logical block size is larger.
+ *
+ * - The entire medium will appear to be a single chip.
+ *
+ * At this writing, MTD uses unsigned 32-bit variables to
+ * represent the size of the medium. If the physical medium is
+ * larger than that, the logical medium must be smaller.
+ *
+ * The NAND Flash MTD code represents the size of a single chip
+ * as an unsigned 32-bit value. It also *requires* that the size
+ * of a chip be a power of two. Thus, the largest possible chip
+ * size is 2GiB.
+ *
+ * When interleaving, the entire medium appears to be one chip.
+ * Thus, when interleaving, the largest possible medium size is
+ * 2GiB.
+ */
+
+ if (physical_medium_size_in_bytes <= max_medium_size_in_bytes) {
+ logical->chip_size =
+ 0x1 << (fls(physical_medium_size_in_bytes) - 1);
+ } else {
+ logical->chip_size =
+ 0x1 << (fls(max_medium_size_in_bytes) - 1);
+ }
+
+ /*
+ * If control arrives here, we're interleaving. The logical
+ * geometry is very different from the physical geometry.
+ */
+
+ logical->chip_count = 1;
+ logical->usable_size = logical->chip_size;
+ logical->block_size =
+ physical->block_size * physical->chip_count;
+ logical->page_data_size =
+ nfc->page_data_size * physical->chip_count;
+
+ /*
+ * Since the logical geometry doesn't match the physical
+ * geometry, we can't use the MTD layout that matches the
+ * NFC geometry. We synthesize one here.
+ *
+ * Our "logical" OOB will be the concatenation of the first 5
+ * bytes of the "physical" OOB of every chip. This has some
+ * important properties:
+ *
+ * - This will make the block mark of every physical chip
+ * visible (even for small page chips, which put their block
+ * mark in the 5th OOB byte).
+ *
+ * - None of the NFC controllers put ECC in the first 5 OOB
+ * bytes, so this layout exposes no ECC.
+ */
+
+ logical->page_oob_size = 5 * physical->chip_count;
+
+ synthetic_layout.eccbytes = 0;
+ synthetic_layout.oobavail = 5 * physical->chip_count;
+ synthetic_layout.oobfree[0].offset = 0;
+ synthetic_layout.oobfree[0].length = synthetic_layout.oobavail;
+
+ /* Install the synthetic layout. */
+
+ logical->mtd_layout = &synthetic_layout;
+
+ }
+
+ /* Compute some interesting facts. */
+
+ block_size_in_pages = logical->block_size / logical->page_data_size;
+ chip_size_in_pages = logical->chip_size / logical->page_data_size;
+ chip_size_in_blocks = logical->chip_size / logical->block_size;
+ usable_medium_size_in_pages =
+ logical->usable_size / logical->page_data_size;
+ usable_medium_size_in_blocks =
+ logical->usable_size / logical->block_size;
+
+ /* Report. */
+
+ dev_dbg(dev, "----------------\n");
+ dev_dbg(dev, "Logical Geometry\n");
+ dev_dbg(dev, "----------------\n");
+ dev_dbg(dev, "Chip Count : %d\n", logical->chip_count);
+ dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
+ logical->page_data_size, logical->page_data_size);
+ dev_dbg(dev, "Page OOB Size in Bytes : %u\n",
+ logical->page_oob_size);
+ dev_dbg(dev, "Block Size in Bytes : %u (0x%x)\n",
+ logical->block_size, logical->block_size);
+ dev_dbg(dev, "Block Size in Pages : %u (0x%x)\n",
+ block_size_in_pages, block_size_in_pages);
+ dev_dbg(dev, "Chip Size in Bytes : %u (0x%x)\n",
+ logical->chip_size, logical->chip_size);
+ dev_dbg(dev, "Chip Size in Pages : %u (0x%x)\n",
+ chip_size_in_pages, chip_size_in_pages);
+ dev_dbg(dev, "Chip Size in Blocks : %u (0x%x)\n",
+ chip_size_in_blocks, chip_size_in_blocks);
+ dev_dbg(dev, "Physical Size in Bytes : %llu (0x%llx)\n",
+ physical_medium_size_in_bytes, physical_medium_size_in_bytes);
+ dev_dbg(dev, "Usable Size in Bytes : %u (0x%x)\n",
+ logical->usable_size, logical->usable_size);
+ dev_dbg(dev, "Usable Size in Pages : %u (0x%x)\n",
+ usable_medium_size_in_pages, usable_medium_size_in_pages);
+ dev_dbg(dev, "Usable Size in Blocks : %u (0x%x)\n",
+ usable_medium_size_in_blocks, usable_medium_size_in_blocks);
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * mal_set_mtd_geometry() - Set up the MTD geometry.
+ *
+ * This function adjusts the owning MTD data structures to match the logical
+ * geometry we've chosen.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_mtd_geometry(struct imx_nfc_data *this)
+{
+ struct logical_geometry *logical = &this->logical_geometry;
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *nand = &this->nand;
+
+ /* Configure the struct mtd_info. */
+
+ mtd->size = logical->usable_size;
+ mtd->erasesize = logical->block_size;
+ mtd->writesize = logical->page_data_size;
+ mtd->ecclayout = logical->mtd_layout;
+ mtd->oobavail = mtd->ecclayout->oobavail;
+ mtd->oobsize = mtd->ecclayout->oobavail + mtd->ecclayout->eccbytes;
+ mtd->subpage_sft = 0; /* We don't support sub-page writing. */
+
+ /* Configure the struct nand_chip. */
+
+ nand->numchips = logical->chip_count;
+ nand->chipsize = logical->chip_size;
+ nand->page_shift = ffs(logical->page_data_size) - 1;
+ nand->pagemask = (nand->chipsize >> nand->page_shift) - 1;
+ nand->subpagesize = mtd->writesize >> mtd->subpage_sft;
+ nand->phys_erase_shift = ffs(logical->block_size) - 1;
+ nand->bbt_erase_shift = nand->phys_erase_shift;
+ nand->chip_shift = ffs(logical->chip_size) - 1;
+ nand->oob_poi = nand->buffers->databuf+logical->page_data_size;
+ nand->ecc.layout = logical->mtd_layout;
+
+ /* Set up the pattern that describes block marks. */
+
+ if (is_small_page_chip(this))
+ nand->badblock_pattern = &small_page_block_mark_descriptor;
+ else
+ nand->badblock_pattern = &large_page_block_mark_descriptor;
+
+ /* Return success. */
+
+ return 0;
+}
+
+/**
+ * mal_set_geometry() - Set up the medium geometry.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_geometry(struct imx_nfc_data *this)
+{
+
+ /* Set up the various layers of geometry, in this specific order. */
+
+ if (mal_set_physical_geometry(this))
+ return !0;
+
+ if (mal_set_nfc_geometry(this))
+ return !0;
+
+ if (mal_set_logical_geometry(this))
+ return !0;
+
+ if (mal_set_mtd_geometry(this))
+ return !0;
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * mal_reset() - Resets the given chip.
+ *
+ * This is the fully-generalized reset operation, including support for
+ * interleaving. All reset operations funnel through here.
+ *
+ * @this: Per-device data.
+ * @chip: The logical chip of interest.
+ */
+static void mal_reset(struct imx_nfc_data *this, unsigned chip)
+{
+ int we_are_interleaving;
+ unsigned int start;
+ unsigned int end;
+ unsigned int i;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_get_status", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+
+ /* Choose the loop bounds. */
+
+ if (we_are_interleaving) {
+ start = 0;
+ end = physical->chip_count;
+ } else {
+ start = chip;
+ end = start + 1;
+ }
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (i = start; i < end; i++) {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, i);
+
+ /* Reset the current chip. */
+
+ add_event("Resetting...", 0);
+
+ nfc_util_send_cmd(this, NAND_CMD_RESET);
+ nfc_util_wait_for_the_nfc(this, false);
+
+ }
+
+ add_event("Exiting mal_get_status", -1);
+
+}
+
+/**
+ * mal_get_status() - Abstracted status retrieval.
+ *
+ * For media with a single chip, or concatenated chips, the HIL explicitly
+ * addresses a single chip at a time and wants the status from that chip only.
+ *
+ * For interleaved media, we must combine the individual chip states. At this
+ * writing, the NAND MTD system knows about the following bits in status
+ * registers:
+ *
+ * +------------------------+-------+---------+
+ * | | | Combine |
+ * | Macro | Value | With |
+ * +------------------------+-------+---------+
+ * | NAND_STATUS_FAIL | 0x01 | OR |
+ * | NAND_STATUS_FAIL_N1 | 0x02 | OR |
+ * | NAND_STATUS_TRUE_READY | 0x20 | AND |
+ * | NAND_STATUS_READY | 0x40 | AND |
+ * | NAND_STATUS_WP | 0x80 | AND |
+ * +------------------------+-------+---------+
+ *
+ * @this: Per-device data.
+ * @chip: The logical chip of interest.
+ */
+static uint8_t mal_get_status(struct imx_nfc_data *this, unsigned chip)
+{
+ int we_are_interleaving;
+ unsigned int start;
+ unsigned int end;
+ unsigned int i;
+ unsigned int x;
+ unsigned int or_mask;
+ unsigned int and_mask;
+ uint8_t status;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_get_status", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+
+ /* Compute the masks we need. */
+
+ or_mask = NAND_STATUS_FAIL | NAND_STATUS_FAIL_N1;
+ and_mask = NAND_STATUS_TRUE_READY | NAND_STATUS_READY | NAND_STATUS_WP;
+
+ /* Assume the chip is successful, ready and writeable. */
+
+ status = and_mask & ~or_mask;
+
+ /* Choose the loop bounds. */
+
+ if (we_are_interleaving) {
+ start = 0;
+ end = physical->chip_count;
+ } else {
+ start = chip;
+ end = start + 1;
+ }
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (i = start; i < end; i++) {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, i);
+
+ /* Get the current chip's status. */
+
+ add_event("Sending the command...", 0);
+
+ nfc_util_send_cmd(this, NAND_CMD_STATUS);
+ nfc_util_wait_for_the_nfc(this, false);
+
+ add_event("Reading the status...", 0);
+
+ x = this->nfc->read_cycle(this);
+
+ /* Fold this chip's status into the combined status. */
+
+ status |= (x & or_mask);
+ status &= (x & and_mask) | or_mask;
+
+ }
+
+ add_event("Exiting mal_get_status", -1);
+
+ return status;
+
+}
+
+/**
+ * mal_read_a_page() - Abstracted page read.
+ *
+ * This function returns the ECC status for the entire read operation. A
+ * positive return value indicates the number of errors that were corrected
+ * (symbol errors for Reed-Solomon hardware engines, bit errors for BCH hardware
+ * engines). A negative return value indicates that the ECC engine failed to
+ * correct all errors and the data is corrupted. A zero return value indicates
+ * there were no errors at all.
+ *
+ * @this: Per-device data.
+ * @use_ecc: Indicates if we're to use ECC.
+ * @chip: The logical chip of interest.
+ * @page: The logical page number to read.
+ * @data: A pointer to the destination data buffer. If this pointer is null,
+ * that indicates the caller doesn't want the data.
+ * @oob: A pointer to the destination OOB buffer. If this pointer is null,
+ * that indicates the caller doesn't want the OOB.
+ */
+static int mal_read_a_page(struct imx_nfc_data *this, int use_ecc,
+ unsigned chip, unsigned page, uint8_t *data, uint8_t *oob)
+{
+ int we_are_interleaving;
+ int use_automatic_op;
+ unsigned int start;
+ unsigned int end;
+ unsigned int current_chip;
+ unsigned int oob_bytes_to_copy;
+ unsigned int data_bytes_to_copy;
+ int status;
+ unsigned int worst_case_ecc_status;
+ int return_value = 0;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct nfc_geometry *nfc = this->nfc_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_read_a_page", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+ use_automatic_op = !!this->nfc->start_auto_read;
+
+ /* Apply the automatic operation override, if any. */
+
+ switch (this->auto_op_override) {
+
+ case NEVER:
+ use_automatic_op = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ if (this->nfc->start_auto_read)
+ use_automatic_op = true;
+ break;
+
+ }
+
+ /* Set up ECC. */
+
+ this->nfc->set_ecc(this, use_ecc);
+
+ /* Check if we're interleaving and set up the loop iterations. */
+
+ if (we_are_interleaving) {
+
+ start = 0;
+ end = physical->chip_count;
+
+ data_bytes_to_copy =
+ this->logical_geometry.page_data_size /
+ this->physical_geometry.chip_count;
+ oob_bytes_to_copy =
+ this->logical_geometry.page_oob_size /
+ this->physical_geometry.chip_count;
+
+ } else {
+
+ start = chip;
+ end = start + 1;
+
+ data_bytes_to_copy = this->logical_geometry.page_data_size;
+ oob_bytes_to_copy = this->logical_geometry.page_oob_size;
+
+ }
+
+ /* If we're using the automatic operation, start it now. */
+
+ if (use_automatic_op) {
+ add_event("Starting the automatic operation...", 0);
+ this->nfc->start_auto_read(this, start, end - start, 0, page);
+ }
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (current_chip = start; current_chip < end; current_chip++) {
+
+ /* Check if we're using the automatic operation. */
+
+ if (use_automatic_op) {
+
+ add_event("Waiting...", 0);
+ this->nfc->wait_for_auto_read(this);
+
+ } else {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, current_chip);
+
+ /* Set up the chip. */
+
+ add_event("Sending the command and addresses...", 0);
+
+ nfc_util_send_cmd_and_addrs(this,
+ NAND_CMD_READ0, 0, page);
+
+ if (is_large_page_chip(this)) {
+ add_event("Sending the final command...", 0);
+ nfc_util_send_cmd(this, NAND_CMD_READSTART);
+ }
+
+ /* Wait till the page is ready. */
+
+ add_event("Waiting for the page to arrive...", 0);
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ /* Read the page. */
+
+ add_event("Reading the page...", 0);
+
+ this->nfc->read_page(this);
+
+ }
+
+ /* Copy a page out of the NFC. */
+
+ add_event("Copying from the NFC...", 0);
+
+ if (oob) {
+ nfc_util_copy_from_the_nfc(this,
+ nfc->page_data_size, oob, oob_bytes_to_copy);
+ oob += oob_bytes_to_copy;
+ }
+
+ if (data) {
+ nfc_util_copy_from_the_nfc(this,
+ 0, data, data_bytes_to_copy);
+ data += data_bytes_to_copy;
+ }
+
+ /*
+ * If we're using ECC, and we haven't already seen an ECC
+ * failure, continue to gather ECC status. Note that, if we
+ * *do* see an ECC failure, we continue to read because the
+ * client might want the data for forensic purposes.
+ */
+
+ if (use_ecc && (return_value >= 0)) {
+
+ add_event("Getting ECC status...", 0);
+
+ status = this->nfc->get_ecc_status(this);
+
+ if (status >= 0)
+ return_value += status;
+ else
+ return_value = -1;
+
+ }
+
+ /* Check if we're using the automatic operation. */
+
+ if (use_automatic_op) {
+
+ /*
+ * If this is not the last iteration, resume the
+ * automatic operation.
+ */
+
+ if (current_chip < (end - 1)) {
+ add_event("Resuming...", 0);
+ this->nfc->resume_auto_read(this);
+ }
+
+ }
+
+ }
+
+ /* Check if we're supposed to inject an ECC error. */
+
+ if (use_ecc && this->inject_ecc_error) {
+
+ /* Inject the appropriate error. */
+
+ if (this->inject_ecc_error < 0) {
+
+ add_event("Injecting an uncorrectable error...", 0);
+
+ return_value = -1;
+
+ } else {
+
+ add_event("Injecting correctable errors...", 0);
+
+ worst_case_ecc_status =
+ physical->chip_count *
+ nfc->buffer_count *
+ nfc->ecc_strength;
+
+ if (this->inject_ecc_error > worst_case_ecc_status)
+ return_value = worst_case_ecc_status;
+ else
+ return_value = this->inject_ecc_error;
+
+ }
+
+ /* Stop injecting further errors. */
+
+ this->inject_ecc_error = 0;
+
+ }
+
+ /* Return. */
+
+ add_event("Exiting mal_read_a_page", -1);
+
+ return return_value;
+
+}
+
+/**
+ * mal_write_a_page() - Abstracted page write.
+ *
+ * This function returns zero if the operation succeeded, or -EIO if the
+ * operation failed.
+ *
+ * @this: Per-device data.
+ * @use_ecc: Indicates if we're to use ECC.
+ * @chip: The logical chip of interest.
+ * @page: The logical page number to write.
+ * @data: A pointer to the source data buffer.
+ * @oob: A pointer to the source OOB buffer.
+ */
+static int mal_write_a_page(struct imx_nfc_data *this, int use_ecc,
+ unsigned chip, unsigned page, const uint8_t *data, const uint8_t *oob)
+{
+ int we_are_interleaving;
+ int use_automatic_op;
+ unsigned int start;
+ unsigned int end;
+ unsigned int current_chip;
+ unsigned int oob_bytes_to_copy;
+ unsigned int data_bytes_to_copy;
+ int return_value = 0;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct nfc_geometry *nfc = this->nfc_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_write_a_page", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+ use_automatic_op = !!this->nfc->start_auto_write;
+
+ /* Apply the automatic operation override, if any. */
+
+ switch (this->auto_op_override) {
+
+ case NEVER:
+ use_automatic_op = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ if (this->nfc->start_auto_write)
+ use_automatic_op = true;
+ break;
+
+ }
+
+ /* Set up ECC. */
+
+ this->nfc->set_ecc(this, use_ecc);
+
+ /* Check if we're interleaving and set up the loop iterations. */
+
+ if (we_are_interleaving) {
+
+ start = 0;
+ end = physical->chip_count;
+
+ data_bytes_to_copy =
+ this->logical_geometry.page_data_size /
+ this->physical_geometry.chip_count;
+ oob_bytes_to_copy =
+ this->logical_geometry.page_oob_size /
+ this->physical_geometry.chip_count;
+
+ } else {
+
+ start = chip;
+ end = start + 1;
+
+ data_bytes_to_copy = this->logical_geometry.page_data_size;
+ oob_bytes_to_copy = this->logical_geometry.page_oob_size;
+
+ }
+
+ /* If we're using the automatic operation, start the hardware now. */
+
+ if (use_automatic_op) {
+ add_event("Starting the automatic operation...", 0);
+ this->nfc->start_auto_write(this, start, end - start, 0, page);
+ }
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (current_chip = start; current_chip < end; current_chip++) {
+
+ /* Copy a page into the NFC. */
+
+ add_event("Copying to the NFC...", 0);
+
+ nfc_util_copy_to_the_nfc(this, oob, nfc->page_data_size,
+ oob_bytes_to_copy);
+ oob += oob_bytes_to_copy;
+
+ nfc_util_copy_to_the_nfc(this, data, 0, data_bytes_to_copy);
+
+ data += data_bytes_to_copy;
+
+ /* Check if we're using the automatic operation. */
+
+ if (use_automatic_op) {
+
+ /* Wait for the write operation to finish. */
+
+ add_event("Waiting...", 0);
+
+ this->nfc->wait_for_auto_write(this);
+
+ } else {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, current_chip);
+
+ /* Set up the chip. */
+
+ add_event("Sending the command and addresses...", 0);
+
+ nfc_util_send_cmd_and_addrs(this,
+ NAND_CMD_SEQIN, 0, page);
+
+ /* Send the page. */
+
+ add_event("Sending the page...", 0);
+
+ this->nfc->send_page(this);
+
+ /* Start programming the page. */
+
+ add_event("Programming the page...", 0);
+
+ nfc_util_send_cmd(this, NAND_CMD_PAGEPROG);
+
+ /* Wait until the page is finished. */
+
+ add_event("Waiting...", 0);
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ }
+
+ }
+
+ /* Get status. */
+
+ add_event("Gathering status...", 0);
+
+ if (mal_get_status(this, chip) & NAND_STATUS_FAIL) {
+ add_event("Bad status", 0);
+ return_value = -EIO;
+ } else {
+ add_event("Good status", 0);
+ }
+
+ /* Return. */
+
+ add_event("Exiting mal_write_a_page", -1);
+
+ return return_value;
+
+}
+
+/**
+ * mal_erase_a_block() - Abstract block erase operation.
+ *
+ * Note that this function does *not* wait for the operation to finish. The
+ * caller is expected to call waitfunc() at some later time.
+ *
+ * @this: Per-device data.
+ * @chip: The logical chip of interest.
+ * @page: A logical page address that identifies the block to erase.
+ */
+static void mal_erase_a_block(struct imx_nfc_data *this,
+ unsigned chip, unsigned page)
+{
+ int we_are_interleaving;
+ int use_automatic_op;
+ unsigned int start;
+ unsigned int end;
+ unsigned int i;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_erase_a_block", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+ use_automatic_op = !!this->nfc->start_auto_erase;
+
+ /* Apply the automatic operation override, if any. */
+
+ switch (this->auto_op_override) {
+
+ case NEVER:
+ use_automatic_op = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ if (this->nfc->start_auto_erase)
+ use_automatic_op = true;
+ break;
+
+ }
+
+ /* Choose the loop bounds. */
+
+ if (we_are_interleaving) {
+ start = 0;
+ end = physical->chip_count;
+ } else {
+ start = chip;
+ end = start + 1;
+ }
+
+ /* Check if we're using the automatic operation. */
+
+ if (use_automatic_op) {
+
+ /*
+ * Start the operation. Note that we don't wait for it to
+ * finish because the HIL will call our waitfunc().
+ */
+
+ add_event("Starting the automatic operation...", 0);
+
+ this->nfc->start_auto_erase(this, start, end - start, page);
+
+ } else {
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (i = start; i < end; i++) {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, i);
+
+ /* Set up the chip. */
+
+ nfc_util_send_cmd_and_addrs(this,
+ NAND_CMD_ERASE1, -1, page);
+
+ /* Start the erase. */
+
+ nfc_util_send_cmd(this, NAND_CMD_ERASE2);
+
+ /*
+ * If this is the last time through the loop, break out
+ * now so we don't try to wait (the HIL will call our
+ * waitfunc() for the final wait).
+ */
+
+ if (i >= (end - 1))
+ break;
+
+ /* Wait for the erase on the current chip to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ }
+
+ }
+
+ add_event("Exiting mal_erase_a_block", -1);
+
+}
+
+/**
+ * mal_is_block_bad() - Abstract bad block check.
+ *
+ * @this: Per-device data.
+ * @chip: The logical chip of interest.
+ * @page: The logical page number to read.
+ */
+ #if 0
+
+/* TODO: Finish this function and plug it in. */
+
+static int mal_is_block_bad(struct imx_nfc_data *this,
+ unsigned chip, unsigned page)
+{
+ int we_are_interleaving;
+ unsigned int start;
+ unsigned int end;
+ unsigned int i;
+ uint8_t *p;
+ int return_value = 0;
+ struct nand_chip *nand = &this->nand;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ /* Figure out if we're interleaving. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+
+ /*
+ * We're about to use the NAND Flash MTD layer's buffer, so invalidate
+ * the page cache.
+ */
+
+ this->nand.pagebuf = -1;
+
+ /*
+ * Read the OOB of the given page, using the NAND Flash MTD's buffer.
+ *
+ * Notice that ECC is off, which it *must* be when scanning block marks.
+ */
+
+ mal_read_a_page(this, false,
+ this->current_chip, this->page_address, 0, nand->oob_poi);
+
+ /* Choose the loop bounds. */
+
+ if (we_are_interleaving) {
+ start = 0;
+ end = physical->chip_count;
+ } else {
+ start = chip;
+ end = start + 1;
+ }
+
+ /* Start scanning at the beginning of the OOB data. */
+
+ p = nand->oob_poi;
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (i = start; i < end; i++, p += 5) {
+
+ /* Examine the OOB for this chip. */
+
+ if (p[nand->badblockpos] != 0xff) {
+ return_value = !0;
+ break;
+ }
+
+ }
+
+ /* Return. */
+
+ return return_value;
+
+}
+#endif
+
+/**
+ * mil_init() - Initializes the MTD Interface Layer.
+ *
+ * @this: Per-device data.
+ */
+static void mil_init(struct imx_nfc_data *this)
+{
+ this->current_chip = -1; /* No chip is selected yet. */
+ this->command_is_new = false; /* No command yet. */
+}
+
+/**
+ * mil_cmd_ctrl() - MTD Interface cmd_ctrl()
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @dat: The data signals to present to the chip.
+ * @ctrl: The control signals to present to the chip.
+ */
+static void mil_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_dev_ready() - MTD Interface dev_ready()
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static int mil_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc dev_ready]\n");
+
+ add_event("Entering mil_dev_ready", 1);
+
+ if (this->nfc->is_ready(this)) {
+ add_event("Exiting mil_dev_ready - Returning ready", -1);
+ return !0;
+ } else {
+ add_event("Exiting mil_dev_ready - Returning busy", -1);
+ return 0;
+ }
+
+}
+
+/**
+ * mil_select_chip() - MTD Interface select_chip()
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @chip: The chip number to select, or -1 to select no chip.
+ */
+static void mil_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc select_chip] chip: %d\n", chip);
+
+ /* Figure out what kind of transition this is. */
+
+ if ((this->current_chip < 0) && (chip >= 0)) {
+ start_event_trace("Entering mil_select_chip");
+ if (this->pdata->force_ce)
+ this->nfc->set_force_ce(this, true);
+ clk_enable(this->clock);
+ add_event("Exiting mil_select_chip", -1);
+ } else if ((this->current_chip >= 0) && (chip < 0)) {
+ add_event("Entering mil_select_chip", 1);
+ if (this->pdata->force_ce)
+ this->nfc->set_force_ce(this, false);
+ clk_disable(this->clock);
+ stop_event_trace("Exiting mil_select_chip");
+ } else {
+ add_event("Entering mil_select_chip", 1);
+ add_event("Exiting mil_select_chip", -1);
+ }
+
+ this->current_chip = chip;
+
+}
+
+/**
+ * mil_cmdfunc() - MTD Interface cmdfunc()
+ *
+ * This function handles NAND Flash command codes from the HIL. Since only the
+ * HIL calls this function (none of the reference implementations we use do), it
+ * needs to handle very few command codes.
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @command: The command code.
+ * @column: The column address associated with this command code, or -1 if no
+ * column address applies.
+ * @page: The page address associated with this command code, or -1 if no
+ * page address applies.
+ */
+static void mil_cmdfunc(struct mtd_info *mtd,
+ unsigned command, int column, int page)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc cmdfunc] command: 0x%02x, "
+ "column: 0x%04x, page: 0x%06x\n", command, column, page);
+
+ add_event("Entering mil_cmdfunc", 1);
+
+ /* Record the command and the fact that it hasn't yet been sent. */
+
+ this->command = command;
+ this->command_is_new = true;
+
+ /*
+ * Process the command code.
+ *
+ * Note the default case to trap unrecognized codes. Thus, every command
+ * we support must have a case here, even if we don't have to do any
+ * pre-processing work. If the HIL changes and starts sending commands
+ * we haven't explicitly implemented, this will warn us.
+ */
+
+ switch (command) {
+
+ case NAND_CMD_READ0:
+ add_event("NAND_CMD_READ0", 0);
+ /*
+ * After calling this function to send the command and
+ * addresses, the HIL will call ecc.read_page() or
+ * ecc.read_page_raw() to collect the data.
+ *
+ * The column address from the HIL is always zero. The only
+ * information we need to keep from this call is the page
+ * address.
+ */
+ this->page_address = page;
+ break;
+
+ case NAND_CMD_STATUS:
+ add_event("NAND_CMD_STATUS", 0);
+ /*
+ * After calling this function to send the command, the HIL
+ * will call read_byte() once to collect the status.
+ */
+ break;
+
+ case NAND_CMD_READID:
+ add_event("NAND_CMD_READID", 0);
+ /*
+ * After calling this function to send the command, the HIL
+ * will call read_byte() repeatedly to collect ID bytes.
+ */
+ break;
+
+ case NAND_CMD_RESET:
+ add_event("NAND_CMD_RESET", 0);
+ mal_reset(this, this->current_chip);
+ break;
+
+ default:
+ dev_emerg(this->dev, "Unsupported NAND Flash command code: "
+ "0x%02x\n", command);
+ BUG();
+ break;
+
+ }
+
+ add_event("Exiting mil_cmdfunc", -1);
+
+}
+
+/**
+ * mil_waitfunc() - MTD Interface waifunc()
+ *
+ * This function blocks until the current chip is ready and then returns the
+ * contents of the chip's status register. The HIL only calls this function
+ * after starting an erase operation.
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ */
+static int mil_waitfunc(struct mtd_info *mtd, struct nand_chip *nand)
+{
+ int status;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc waitfunc]\n");
+
+ add_event("Entering mil_waitfunc", 1);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ /* Get the status. */
+
+ status = mal_get_status(this, this->current_chip);
+
+ add_event("Exiting mil_waitfunc", -1);
+
+ return status;
+
+}
+
+/**
+ * mil_read_byte() - MTD Interface read_byte().
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static uint8_t mil_read_byte(struct mtd_info *mtd)
+{
+ uint8_t byte = 0;
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ add_event("Entering mil_read_byte", 1);
+
+ /*
+ * The command sent by the HIL before it called this function determines
+ * how we get the byte we're going to return.
+ */
+
+ switch (this->command) {
+
+ case NAND_CMD_STATUS:
+ add_event("NAND_CMD_STATUS", 0);
+ byte = mal_get_status(this, this->current_chip);
+ break;
+
+ case NAND_CMD_READID:
+ add_event("NAND_CMD_READID", 0);
+
+ /*
+ * Check if the command is new. If so, then the HIL just
+ * recently called cmdfunc(), so the current chip isn't selected
+ * and the command hasn't been sent to the chip.
+ */
+
+ if (this->command_is_new) {
+ add_event("Sending the \"Read ID\" command...", 0);
+ this->nfc->select_chip(this, this->current_chip);
+ nfc_util_send_cmd_and_addrs(this,
+ NAND_CMD_READID, 0, -1);
+ this->command_is_new = false;
+ }
+
+ /* Read the ID byte. */
+
+ add_event("Reading the ID byte...", 0);
+
+ byte = this->nfc->read_cycle(this);
+
+ break;
+
+ default:
+ dev_emerg(this->dev, "Unsupported NAND Flash command code: "
+ "0x%02x\n", this->command);
+ BUG();
+ break;
+
+ }
+
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "[imx_nfc read_byte] Returning: 0x%02x\n", byte);
+
+ add_event("Exiting mil_read_byte", -1);
+
+ return byte;
+
+}
+
+/**
+ * mil_read_word() - MTD Interface read_word().
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static uint16_t mil_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+ return 0;
+}
+
+/**
+ * mil_read_buf() - MTD Interface read_buf().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @buf: The destination buffer.
+ * @len: The number of bytes to read.
+ */
+static void mil_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_write_buf() - MTD Interface write_buf().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @buf: The source buffer.
+ * @len: The number of bytes to read.
+ */
+static void mil_write_buf(struct mtd_info *mtd, const uint8_t * buf, int len)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_verify_buf() - MTD Interface verify_buf().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @buf: The destination buffer.
+ * @len: The number of bytes to read.
+ */
+static int mil_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+ return 0;
+}
+
+/**
+ * mil_ecc_hwctl() - MTD Interface ecc.hwctl().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @mode: The ECC mode.
+ */
+static void mil_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_ecc_calculate() - MTD Interface ecc.calculate().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @dat: A pointer to the source data.
+ * @ecc_code: A pointer to a buffer that will receive the resulting ECC.
+ */
+static int mil_ecc_calculate(struct mtd_info *mtd,
+ const uint8_t *dat, uint8_t *ecc_code)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+ return 0;
+}
+
+/**
+ * mil_ecc_correct() - MTD Interface ecc.correct().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @dat: A pointer to the source data.
+ * @read_ecc: A pointer to the ECC that was read from the medium.
+ * @calc_ecc: A pointer to the ECC that was calculated for the source data.
+ */
+static int mil_ecc_correct(struct mtd_info *mtd,
+ uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+ return 0;
+}
+
+/**
+ * mil_ecc_read_page() - MTD Interface ecc.read_page().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the destination buffer.
+ */
+static int mil_ecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *nand, uint8_t *buf)
+{
+ int ecc_status;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_page]\n");
+
+ add_event("Entering mil_ecc_read_page", 1);
+
+ /* Read the page. */
+
+ ecc_status =
+ mal_read_a_page(this, true, this->current_chip,
+ this->page_address, buf, nand->oob_poi);
+
+ /* Propagate ECC information. */
+
+ if (ecc_status < 0) {
+ add_event("ECC Failure", 0);
+ mtd->ecc_stats.failed++;
+ } else if (ecc_status > 0) {
+ add_event("ECC Corrections", 0);
+ mtd->ecc_stats.corrected += ecc_status;
+ }
+
+ add_event("Exiting mil_ecc_read_page", -1);
+
+ return 0;
+
+}
+
+/**
+ * mil_ecc_read_page_raw() - MTD Interface ecc.read_page_raw().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the destination buffer.
+ */
+static int mil_ecc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *nand, uint8_t *buf)
+{
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_page_raw]\n");
+
+ add_event("Entering mil_ecc_read_page_raw", 1);
+
+ mal_read_a_page(this, false, this->current_chip,
+ this->page_address, buf, nand->oob_poi);
+
+ add_event("Exiting mil_ecc_read_page_raw", -1);
+
+ return 0;
+
+}
+
+/**
+ * mil_ecc_write_page() - MTD Interface ecc.write_page().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the source buffer.
+ */
+static void mil_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *nand, const uint8_t *buf)
+{
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_ecc_write_page_raw() - MTD Interface ecc.write_page_raw().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the source buffer.
+ */
+static void mil_ecc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *nand, const uint8_t *buf)
+{
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_write_page() - MTD Interface ecc.write_page().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the source buffer.
+ * @page: The page number to write.
+ * @cached: Indicates cached programming (ignored).
+ * @raw: Indicates not to use ECC.
+ */
+static int mil_write_page(struct mtd_info *mtd,
+ struct nand_chip *nand, const uint8_t *buf,
+ int page, int cached, int raw)
+{
+ int return_value;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc write_page]\n");
+
+ add_event("Entering mil_write_page", 1);
+
+ return_value = mal_write_a_page(this, !raw,
+ this->current_chip, page, buf, nand->oob_poi);
+
+ add_event("Exiting mil_write_page", -1);
+
+ return return_value;
+
+}
+
+/**
+ * mil_ecc_read_oob() - MTD Interface read_oob().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @page: The page number to read.
+ * @sndcmd: Indicates this function should send a command to the chip before
+ * reading the out-of-band bytes. This is only false for small page
+ * chips that support auto-increment.
+ */
+static int mil_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
+ int page, int sndcmd)
+{
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_oob] "
+ "page: 0x%06x, sndcmd: %s\n", page, sndcmd ? "Yes" : "No");
+
+ add_event("Entering mil_ecc_read_oob", 1);
+
+ mal_read_a_page(this, false,
+ this->current_chip, page, 0, nand->oob_poi);
+
+ add_event("Exiting mil_ecc_read_oob", -1);
+
+ /*
+ * Return true, indicating that the next call to this function must send
+ * a command.
+ */
+
+ return true;
+
+}
+
+/**
+ * mil_ecc_write_oob() - MTD Interface write_oob().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @page: The page number to write.
+ */
+static int mil_ecc_write_oob(struct mtd_info *mtd,
+ struct nand_chip *nand, int page)
+{
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_write_oob] page: 0x%06x\n", page);
+
+ /*
+ * There are fundamental incompatibilities between the i.MX NFC and the
+ * NAND Flash MTD model that make it essentially impossible to write the
+ * out-of-band bytes.
+ */
+
+ dev_emerg(this->dev, "This driver doesn't support writing the OOB\n");
+ WARN_ON(1);
+
+ /* Return status. */
+
+ return -EIO;
+
+}
+
+/**
+ * mil_erase_cmd() - MTD Interface erase_cmd().
+ *
+ * We set the erase_cmd pointer in struct nand_chip to point to this function.
+ * Thus, the HIL will call here for all erase operations.
+ *
+ * Strictly speaking, since the erase_cmd pointer is marked "Internal," we
+ * shouldn't be doing this. However, the only reason the HIL uses that pointer
+ * is to install a different function for erasing conventional NAND Flash or AND
+ * Flash. Since AND Flash is obsolete and we don't support it, this isn't
+ * important.
+ *
+ * Furthermore, to cleanly implement interleaving (which is critical to speeding
+ * up erase operations), we want to "hook into" the operation at the highest
+ * semantic level possible. If we don't hook this function, then the only way
+ * we'll know that an erase is happening is when the HIL calls cmdfunc() with
+ * an erase command. Implementing interleaving at that level is roughly a
+ * billion times less desirable.
+ *
+ * This function does *not* wait for the operation to finish. The HIL will call
+ * waitfunc() later to wait for the operation to finish.
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @page: A page address that identifies the block to erase.
+ */
+static void mil_erase_cmd(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc erase_cmd] page: 0x%06x\n", page);
+
+ add_event("Entering mil_erase_cmd", 1);
+
+ mal_erase_a_block(this, this->current_chip, page);
+
+ add_event("Exiting mil_erase_cmd", -1);
+
+}
+
+/**
+ * mil_block_bad() - MTD Interface block_bad().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @ofs: The offset of the block of interest, from the start of the medium.
+ * @getchip: Indicates this function must acquire the MTD before using it.
+ */
+#if 0
+
+/* TODO: Finish this function and plug it in. */
+
+static int mil_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ unsigned int chip;
+ unsigned int page;
+ int return_value;
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc block_bad] page: 0x%06x\n", page);
+
+ add_event("Entering mil_block_bad", 1);
+
+ /* Compute the logical chip number that contains the given offset. */
+
+ chip = (unsigned int) (ofs >> nand->chip_shift);
+
+ /* Compute the logical page address within the logical chip. */
+
+ page = ((unsigned int) (ofs >> nand->page_shift)) & nand->pagemask;
+
+ /* Check if the block is bad. */
+
+ return_value = mal_is_block_bad(this, chip, page);
+
+ if (return_value)
+ add_event("Bad block", 0);
+
+ /* Return. */
+
+ add_event("Exiting mil_block_bad", -1);
+
+ return return_value;
+
+}
+#endif
+
+/**
+ * mil_scan_bbt() - MTD Interface scan_bbt().
+ *
+ * The HIL calls this function once, when it initializes the NAND Flash MTD.
+ *
+ * Nominally, the purpose of this function is to look for or create the bad
+ * block table. In fact, since the HIL calls this function at the very end of
+ * the initialization process started by nand_scan(), and the HIL doesn't have a
+ * more formal mechanism, everyone "hooks" this function to continue the
+ * initialization process.
+ *
+ * At this point, the physical NAND Flash chips have been identified and
+ * counted, so we know the physical geometry. This enables us to make some
+ * important configuration decisions.
+ *
+ * The return value of this function propogates directly back to this driver's
+ * call to nand_scan(). Anything other than zero will cause this driver to
+ * tear everything down and declare failure.
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static int mil_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc scan_bbt] \n");
+
+ add_event("Entering mil_scan_bbt", 1);
+
+ /*
+ * We replace the erase_cmd() function that the MTD NAND Flash system
+ * has installed with our own. See mil_erase_cmd() for the reasons.
+ */
+
+ nand->erase_cmd = mil_erase_cmd;
+
+ /*
+ * Tell MTD users that the out-of-band area can't be written.
+ *
+ * This flag is not part of the standard kernel source tree. It comes
+ * from a patch that touches both MTD and JFFS2.
+ *
+ * The problem is that, without this patch, JFFS2 believes it can write
+ * the data area and the out-of-band area separately. This is wrong for
+ * two reasons:
+ *
+ * 1) Our NFC distributes out-of-band bytes throughout the page,
+ * intermingled with the data, and covered by the same ECC.
+ * Thus, it's not possible to write the out-of-band bytes and
+ * data bytes separately.
+ *
+ * 2) Large page (MLC) Flash chips don't support partial page
+ * writes. You must write the entire page at a time. Thus, even
+ * if our NFC didn't force you to write out-of-band and data
+ * bytes together, it would *still* be a bad idea to do
+ * otherwise.
+ */
+
+ mtd->flags &= ~MTD_OOB_WRITEABLE;
+
+ /* Set up geometry. */
+
+ mal_set_geometry(this);
+
+ /* We use the reference implementation for bad block management. */
+
+ add_event("Exiting mil_scan_bbt", -1);
+
+ return nand_scan_bbt(mtd, nand->badblock_pattern);
+
+}
+
+/**
+ * parse_bool_param() - Parses the value of a boolean parameter string.
+ *
+ * @s: The string to parse.
+ */
+static int parse_bool_param(const char *s)
+{
+
+ if (!strcmp(s, "1") || !strcmp(s, "on") ||
+ !strcmp(s, "yes") || !strcmp(s, "true")) {
+ return 1;
+ } else if (!strcmp(s, "0") || !strcmp(s, "off") ||
+ !strcmp(s, "no") || !strcmp(s, "false")) {
+ return 0;
+ } else {
+ return -1;
+ }
+
+}
+
+/**
+ * set_module_enable() - Controls whether this driver is enabled.
+ *
+ * Note that this state can be controlled from the command line. Disabling this
+ * driver is sometimes useful for debugging.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_enable(const char *s, struct kernel_param *kp)
+{
+
+ switch (parse_bool_param(s)) {
+
+ case 1:
+ imx_nfc_module_enable = true;
+ break;
+
+ case 0:
+ imx_nfc_module_enable = false;
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * get_module_enable() - Indicates whether this driver is enabled.
+ *
+ * @p: A pointer to a (small) buffer that will receive the response.
+ * @kp: The owning kernel parameter.
+ */
+static int get_module_enable(char *p, struct kernel_param *kp)
+{
+ p[0] = imx_nfc_module_enable ? '1' : '0';
+ p[1] = 0;
+ return 1;
+}
+
+#ifdef EVENT_REPORTING
+
+/**
+ * set_module_report_events() - Controls whether this driver reports events.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_report_events(const char *s, struct kernel_param *kp)
+{
+
+ switch (parse_bool_param(s)) {
+
+ case 1:
+ imx_nfc_module_report_events = true;
+ break;
+
+ case 0:
+ imx_nfc_module_report_events = false;
+ reset_event_trace();
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * get_module_report_events() - Indicates whether the driver reports events.
+ *
+ * @p: A pointer to a (small) buffer that will receive the response.
+ * @kp: The owning kernel parameter.
+ */
+static int get_module_report_events(char *p, struct kernel_param *kp)
+{
+ p[0] = imx_nfc_module_report_events ? '1' : '0';
+ p[1] = 0;
+ return 1;
+}
+
+/**
+ * set_module_dump_events() - Forces the driver to dump current events.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_dump_events(const char *s, struct kernel_param *kp)
+{
+ dump_event_trace();
+ return 0;
+}
+
+#endif /*EVENT_REPORTING*/
+
+/**
+ * set_module_interleave_override() - Controls the interleave override.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_interleave_override(const char *s,
+ struct kernel_param *kp)
+{
+
+ if (!strcmp(s, "-1"))
+ imx_nfc_module_interleave_override = NEVER;
+ else if (!strcmp(s, "0"))
+ imx_nfc_module_interleave_override = DRIVER_CHOICE;
+ else if (!strcmp(s, "1"))
+ imx_nfc_module_interleave_override = ALWAYS;
+ else
+ return -EINVAL;
+
+ return 0;
+
+}
+
+/**
+ * get_module_interleave_override() - Indicates the interleave override state.
+ *
+ * @p: A pointer to a (small) buffer that will receive the response.
+ * @kp: The owning kernel parameter.
+ */
+static int get_module_interleave_override(char *p, struct kernel_param *kp)
+{
+ return sprintf(p, "%d", imx_nfc_module_interleave_override);
+}
+
+/**
+ * set_force_bytewise_copy() - Controls forced bytewise copy from/to the NFC.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_force_bytewise_copy(const char *s,
+ struct kernel_param *kp)
+{
+
+ switch (parse_bool_param(s)) {
+
+ case 1:
+ imx_nfc_module_force_bytewise_copy = true;
+ break;
+
+ case 0:
+ imx_nfc_module_force_bytewise_copy = false;
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * get_force_bytewise_copy() - Indicates whether bytewise copy is being forced.
+ *
+ * @p: A pointer to a (small) buffer that will receive the response.
+ * @kp: The owning kernel parameter.
+ */
+static int get_module_force_bytewise_copy(char *p, struct kernel_param *kp)
+{
+ p[0] = imx_nfc_module_force_bytewise_copy ? '1' : '0';
+ p[1] = 0;
+ return 1;
+}
+
+/* Module attributes that appear in sysfs. */
+
+module_param_call(enable, set_module_enable, get_module_enable, 0, 0444);
+MODULE_PARM_DESC(enable, "enables/disables probing");
+
+#ifdef EVENT_REPORTING
+module_param_call(report_events,
+ set_module_report_events, get_module_report_events, 0, 0644);
+MODULE_PARM_DESC(report_events, "enables/disables event reporting");
+
+module_param_call(dump_events, set_module_dump_events, 0, 0, 0644);
+MODULE_PARM_DESC(dump_events, "forces current event dump");
+#endif
+
+module_param_call(interleave_override, set_module_interleave_override,
+ get_module_interleave_override, 0, 0444);
+MODULE_PARM_DESC(interleave_override, "overrides interleaving choice");
+
+module_param_call(force_bytewise_copy, set_module_force_bytewise_copy,
+ get_module_force_bytewise_copy, 0, 0644);
+MODULE_PARM_DESC(force_bytewise_copy, "forces bytewise copy from/to NFC");
+
+/**
+ * show_device_platform_info() - Shows the device's platform information.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_platform_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int o = 0;
+ unsigned int i;
+ void *buffer_base;
+ void *primary_base;
+ void *secondary_base;
+ unsigned int interrupt_number;
+ struct resource *r;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct platform_device *pdev = this->pdev;
+ struct imx_nfc_platform_data *pdata = this->pdata;
+ struct mtd_partition *partition;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_BUFFERS_ADDR_RES_NAME);
+
+ buffer_base = (void *) r->start;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);
+
+ primary_base = (void *) r->start;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
+
+ secondary_base = (void *) r->start;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ IMX_NFC_INTERRUPT_RES_NAME);
+
+ interrupt_number = r->start;
+
+ o += sprintf(buf,
+ "NFC Major Version : %u\n"
+ "NFC Minor Version : %u\n"
+ "Force CE : %s\n"
+ "Target Cycle in ns : %u\n"
+ "Clock Name : %s\n"
+ "Interleave : %s\n"
+ "Buffer Base : 0x%p\n"
+ "Primary Registers Base : 0x%p\n"
+ "Secondary Registers Base: 0x%p\n"
+ "Interrupt Number : %u\n"
+ ,
+ pdata->nfc_major_version,
+ pdata->nfc_minor_version,
+ pdata->force_ce ? "Yes" : "No",
+ pdata->target_cycle_in_ns,
+ pdata->clock_name,
+ pdata->interleave ? "Yes" : "No",
+ buffer_base,
+ primary_base,
+ secondary_base,
+ interrupt_number
+ );
+
+ #ifdef CONFIG_MTD_PARTITIONS
+
+ o += sprintf(buf + o,
+ "Partition Count : %u\n"
+ ,
+ pdata->partition_count
+ );
+
+ /* Loop over partitions. */
+
+ for (i = 0; i < pdata->partition_count; i++) {
+
+ partition = pdata->partitions + i;
+
+ o += sprintf(buf+o, " [%d]\n", i);
+ o += sprintf(buf+o, " Name : %s\n", partition->name);
+
+ switch (partition->offset) {
+
+ case MTDPART_OFS_NXTBLK:
+ o += sprintf(buf+o, " Offset: "
+ "MTDPART_OFS_NXTBLK\n");
+ break;
+ case MTDPART_OFS_APPEND:
+ o += sprintf(buf+o, " Offset: "
+ "MTDPART_OFS_APPEND\n");
+ break;
+ default:
+ o += sprintf(buf+o, " Offset: %u (%u MiB)\n",
+ partition->offset,
+ partition->offset / (1024 * 1024));
+ break;
+
+ }
+
+ if (partition->size == MTDPART_SIZ_FULL) {
+ o += sprintf(buf+o, " Size : "
+ "MTDPART_SIZ_FULL\n");
+ } else {
+ o += sprintf(buf+o, " Size : %u (%u MiB)\n",
+ partition->size,
+ partition->size / (1024 * 1024));
+ }
+
+ }
+
+ #endif
+
+ return o;
+
+}
+
+/**
+ * show_device_physical_geometry() - Shows the physical Flash device geometry.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_physical_geometry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct physical_geometry *physical = &this->physical_geometry;
+
+ return sprintf(buf,
+ "Chip Count : %u\n"
+ "Chip Size in Bytes : %llu\n"
+ "Block Size in Bytes : %u\n"
+ "Page Data Size in Bytes: %u\n"
+ "Page OOB Size in Bytes : %u\n"
+ ,
+ physical->chip_count,
+ physical->chip_size,
+ physical->block_size,
+ physical->page_data_size,
+ physical->page_oob_size
+ );
+
+}
+
+/**
+ * show_device_nfc_info() - Shows the NFC-specific information.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_nfc_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long parent_clock_rate_in_hz;
+ unsigned long clock_rate_in_hz;
+ struct clk *parent_clock;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct nfc_hal *nfc = this->nfc;
+
+ parent_clock = clk_get_parent(this->clock);
+ parent_clock_rate_in_hz = clk_get_rate(parent_clock);
+ clock_rate_in_hz = clk_get_rate(this->clock);
+
+ return sprintf(buf,
+ "Major Version : %u\n"
+ "Minor Version : %u\n"
+ "Max Chip Count : %u\n"
+ "Max Buffer Count : %u\n"
+ "Spare Buffer Stride : %u\n"
+ "Has Secondary Registers : %s\n"
+ "Can Be Symmetric : %s\n"
+ "Exposes Ready/Busy : %s\n"
+ "Parent Clock Rate in Hz : %lu\n"
+ "Clock Rate in Hz : %lu\n"
+ "Symmetric Clock : %s\n"
+ ,
+ nfc->major_version,
+ nfc->minor_version,
+ nfc->max_chip_count,
+ nfc->max_buffer_count,
+ nfc->spare_buf_stride,
+ nfc->has_secondary_regs ? "Yes" : "No",
+ nfc->can_be_symmetric ? "Yes" : "No",
+ nfc->is_ready ? "Yes" : "No",
+ parent_clock_rate_in_hz,
+ clock_rate_in_hz,
+ this->nfc->get_symmetric(this) ? "Yes" : "No"
+ );
+
+}
+
+/**
+ * show_device_nfc_geometry() - Shows the NFC view of the device geometry.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_nfc_geometry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ return sprintf(buf,
+ "Page Data Size in Bytes : %u\n"
+ "Page OOB Size in Bytes : %u\n"
+ "ECC Algorithm : %s\n"
+ "ECC Strength : %u\n"
+ "Buffers in Use : %u\n"
+ "Spare Buffer Size in Use: %u\n"
+ "Spare Buffer Spillover : %u\n"
+ ,
+ this->nfc_geometry->page_data_size,
+ this->nfc_geometry->page_oob_size,
+ this->nfc_geometry->ecc_algorithm,
+ this->nfc_geometry->ecc_strength,
+ this->nfc_geometry->buffer_count,
+ this->nfc_geometry->spare_buf_size,
+ this->nfc_geometry->spare_buf_spill
+ );
+
+}
+
+/**
+ * show_device_logical_geometry() - Shows the logical device geometry.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_logical_geometry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ return sprintf(buf,
+ "Chip Count : %u\n"
+ "Chip Size in Bytes : %u\n"
+ "Usable Size in Bytes : %u\n"
+ "Block Size in Bytes : %u\n"
+ "Page Data Size in Bytes: %u\n"
+ "Page OOB Size in Bytes : %u\n"
+ ,
+ logical->chip_count,
+ logical->chip_size,
+ logical->usable_size,
+ logical->block_size,
+ logical->page_data_size,
+ logical->page_oob_size
+ );
+
+}
+
+/**
+ * show_device_mtd_nand_info() - Shows the device's MTD NAND-specific info.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_mtd_nand_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int o = 0;
+ unsigned int i;
+ unsigned int j;
+ static const unsigned int columns = 8;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct nand_chip *nand = &this->nand;
+
+ o += sprintf(buf + o,
+ "Options : 0x%08x\n"
+ "Chip Count : %u\n"
+ "Chip Size : %lu\n"
+ "Minimum Writable Size: %u\n"
+ "Page Shift : %u\n"
+ "Page Mask : 0x%x\n"
+ "Block Shift : %u\n"
+ "BBT Block Shift : %u\n"
+ "Chip Shift : %u\n"
+ "Block Mark Offset : %u\n"
+ "Cached Page Number : %d\n"
+ ,
+ nand->options,
+ nand->numchips,
+ nand->chipsize,
+ nand->subpagesize,
+ nand->page_shift,
+ nand->pagemask,
+ nand->phys_erase_shift,
+ nand->bbt_erase_shift,
+ nand->chip_shift,
+ nand->badblockpos,
+ nand->pagebuf
+ );
+
+ o += sprintf(buf + o,
+ "ECC Byte Count : %u\n"
+ ,
+ nand->ecc.layout->eccbytes
+ );
+
+ /* Loop over rows. */
+
+ for (i = 0; (i * columns) < nand->ecc.layout->eccbytes; i++) {
+
+ /* Loop over columns within rows. */
+
+ for (j = 0; j < columns; j++) {
+
+ if (((i * columns) + j) >= nand->ecc.layout->eccbytes)
+ break;
+
+ o += sprintf(buf + o, " %3u",
+ nand->ecc.layout->eccpos[(i * columns) + j]);
+
+ }
+
+ o += sprintf(buf + o, "\n");
+
+ }
+
+ o += sprintf(buf + o,
+ "OOB Available Bytes : %u\n"
+ ,
+ nand->ecc.layout->oobavail
+ );
+
+ j = 0;
+
+ for (i = 0; j < nand->ecc.layout->oobavail; i++) {
+
+ j += nand->ecc.layout->oobfree[i].length;
+
+ o += sprintf(buf + o,
+ " [%3u, %2u]\n"
+ ,
+ nand->ecc.layout->oobfree[i].offset,
+ nand->ecc.layout->oobfree[i].length
+ );
+
+ }
+
+ return o;
+
+}
+
+/**
+ * show_device_mtd_info() - Shows the device's MTD-specific information.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_mtd_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int o = 0;
+ unsigned int i;
+ unsigned int j;
+ static const unsigned int columns = 8;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct mtd_info *mtd = &this->mtd;
+
+ o += sprintf(buf + o,
+ "Name : %s\n"
+ "Type : %u\n"
+ "Flags : 0x%08x\n"
+ "Size in Bytes : %u\n"
+ "Erase Region Count : %d\n"
+ "Erase Size in Bytes: %u\n"
+ "Write Size in Bytes: %u\n"
+ "OOB Size in Bytes : %u\n"
+ "Errors Corrected : %u\n"
+ "Failed Reads : %u\n"
+ "Bad Block Count : %u\n"
+ "BBT Block Count : %u\n"
+ ,
+ mtd->name,
+ mtd->type,
+ mtd->flags,
+ mtd->size,
+ mtd->numeraseregions,
+ mtd->erasesize,
+ mtd->writesize,
+ mtd->oobsize,
+ mtd->ecc_stats.corrected,
+ mtd->ecc_stats.failed,
+ mtd->ecc_stats.badblocks,
+ mtd->ecc_stats.bbtblocks
+ );
+
+ o += sprintf(buf + o,
+ "ECC Byte Count : %u\n"
+ ,
+ mtd->ecclayout->eccbytes
+ );
+
+ /* Loop over rows. */
+
+ for (i = 0; (i * columns) < mtd->ecclayout->eccbytes; i++) {
+
+ /* Loop over columns within rows. */
+
+ for (j = 0; j < columns; j++) {
+
+ if (((i * columns) + j) >= mtd->ecclayout->eccbytes)
+ break;
+
+ o += sprintf(buf + o, " %3u",
+ mtd->ecclayout->eccpos[(i * columns) + j]);
+
+ }
+
+ o += sprintf(buf + o, "\n");
+
+ }
+
+ o += sprintf(buf + o,
+ "OOB Available Bytes: %u\n"
+ ,
+ mtd->ecclayout->oobavail
+ );
+
+ j = 0;
+
+ for (i = 0; j < mtd->ecclayout->oobavail; i++) {
+
+ j += mtd->ecclayout->oobfree[i].length;
+
+ o += sprintf(buf + o,
+ " [%3u, %2u]\n"
+ ,
+ mtd->ecclayout->oobfree[i].offset,
+ mtd->ecclayout->oobfree[i].length
+ );
+
+ }
+
+ return o;
+
+}
+
+/**
+ * show_device_bbt_pages() - Shows the pages in which BBT's appear.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_bbt_pages(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int o = 0;
+ unsigned int i;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct nand_chip *nand = &this->nand;
+
+ /* Loop over main BBT pages. */
+
+ if (nand->bbt_td)
+ for (i = 0; i < NAND_MAX_CHIPS; i++)
+ o += sprintf(buf + o, "%d: 0x%08x\n",
+ i, nand->bbt_td->pages[i]);
+
+ /* Loop over mirror BBT pages. */
+
+ if (nand->bbt_md)
+ for (i = 0; i < NAND_MAX_CHIPS; i++)
+ o += sprintf(buf + o, "%d: 0x%08x\n",
+ i, nand->bbt_md->pages[i]);
+
+ return o;
+
+}
+
+/**
+ * show_device_cycle_in_ns() - Shows the device's cycle in ns.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_cycle_in_ns(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", get_cycle_in_ns(this));
+}
+
+/**
+ * store_device_cycle_in_ns() - Sets the device's cycle in ns.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_cycle_in_ns(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int error;
+ unsigned long new_cycle_in_ns;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ /* Look for nonsense. */
+
+ if (!size)
+ return -EINVAL;
+
+ /* Try to understand the new cycle period. */
+
+ if (strict_strtoul(buf, 0, &new_cycle_in_ns))
+ return -EINVAL;
+
+ /* Try to implement the new cycle period. */
+
+ error = this->nfc->set_closest_cycle(this, new_cycle_in_ns);
+
+ if (error)
+ return -EINVAL;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/**
+ * show_device_interrupt_override() - Shows the device's interrupt override.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_interrupt_override(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ switch (this->interrupt_override) {
+
+ case NEVER:
+ return sprintf(buf, "-1\n");
+ break;
+
+ case DRIVER_CHOICE:
+ return sprintf(buf, "0\n");
+ break;
+
+ case ALWAYS:
+ return sprintf(buf, "1\n");
+ break;
+
+ default:
+ return sprintf(buf, "?\n");
+ break;
+
+ }
+
+}
+
+/**
+ * store_device_interrupt_override() - Sets the device's interrupt override.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_interrupt_override(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ if (!strcmp(buf, "-1"))
+ this->interrupt_override = NEVER;
+ else if (!strcmp(buf, "0"))
+ this->interrupt_override = DRIVER_CHOICE;
+ else if (!strcmp(buf, "1"))
+ this->interrupt_override = ALWAYS;
+ else
+ return -EINVAL;
+
+ return size;
+
+}
+
+/**
+ * show_device_auto_op_override() - Shows the device's automatic op override.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_auto_op_override(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ switch (this->auto_op_override) {
+
+ case NEVER:
+ return sprintf(buf, "-1\n");
+ break;
+
+ case DRIVER_CHOICE:
+ return sprintf(buf, "0\n");
+ break;
+
+ case ALWAYS:
+ return sprintf(buf, "1\n");
+ break;
+
+ default:
+ return sprintf(buf, "?\n");
+ break;
+
+ }
+
+}
+
+/**
+ * store_device_auto_op_override() - Sets the device's automatic op override.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_auto_op_override(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ if (!strcmp(buf, "-1"))
+ this->auto_op_override = NEVER;
+ else if (!strcmp(buf, "0"))
+ this->auto_op_override = DRIVER_CHOICE;
+ else if (!strcmp(buf, "1"))
+ this->auto_op_override = ALWAYS;
+ else
+ return -EINVAL;
+
+ return size;
+
+}
+
+/**
+ * show_device_inject_ecc_error() - Shows the device's error injection flag.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_inject_ecc_error(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", this->inject_ecc_error);
+
+}
+
+/**
+ * store_device_inject_ecc_error() - Sets the device's error injection flag.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_inject_ecc_error(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ unsigned long new_inject_ecc_error;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ /* Look for nonsense. */
+
+ if (!size)
+ return -EINVAL;
+
+ /* Try to understand the new cycle period. */
+
+ if (strict_strtol(buf, 0, &new_inject_ecc_error))
+ return -EINVAL;
+
+ /* Store the value. */
+
+ this->inject_ecc_error = new_inject_ecc_error;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/**
+ * store_device_invalidate_page_cache() - Invalidates the device's page cache.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_invalidate_page_cache(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ /* Invalidate the page cache. */
+
+ this->nand.pagebuf = -1;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/* Device attributes that appear in sysfs. */
+
+static DEVICE_ATTR(platform_info , 0444, show_device_platform_info , 0);
+static DEVICE_ATTR(physical_geometry, 0444, show_device_physical_geometry, 0);
+static DEVICE_ATTR(nfc_info , 0444, show_device_nfc_info , 0);
+static DEVICE_ATTR(nfc_geometry , 0444, show_device_nfc_geometry , 0);
+static DEVICE_ATTR(logical_geometry , 0444, show_device_logical_geometry , 0);
+static DEVICE_ATTR(mtd_nand_info , 0444, show_device_mtd_nand_info , 0);
+static DEVICE_ATTR(mtd_info , 0444, show_device_mtd_info , 0);
+static DEVICE_ATTR(bbt_pages , 0444, show_device_bbt_pages , 0);
+
+static DEVICE_ATTR(cycle_in_ns, 0644,
+ show_device_cycle_in_ns, store_device_cycle_in_ns);
+
+static DEVICE_ATTR(interrupt_override, 0644,
+ show_device_interrupt_override, store_device_interrupt_override);
+
+static DEVICE_ATTR(auto_op_override, 0644,
+ show_device_auto_op_override, store_device_auto_op_override);
+
+static DEVICE_ATTR(inject_ecc_error, 0644,
+ show_device_inject_ecc_error, store_device_inject_ecc_error);
+
+static DEVICE_ATTR(invalidate_page_cache, 0644,
+ 0, store_device_invalidate_page_cache);
+
+static struct device_attribute *device_attributes[] = {
+ &dev_attr_platform_info,
+ &dev_attr_physical_geometry,
+ &dev_attr_nfc_info,
+ &dev_attr_nfc_geometry,
+ &dev_attr_logical_geometry,
+ &dev_attr_mtd_nand_info,
+ &dev_attr_mtd_info,
+ &dev_attr_bbt_pages,
+ &dev_attr_cycle_in_ns,
+ &dev_attr_interrupt_override,
+ &dev_attr_auto_op_override,
+ &dev_attr_inject_ecc_error,
+ &dev_attr_invalidate_page_cache,
+};
+
+/**
+ * validate_the_platform() - Validates information about the platform.
+ *
+ * Note that this function doesn't validate the NFC version. That's done when
+ * the probing process attempts to configure for the specific hardware version.
+ *
+ * @pdev: A pointer to the platform device.
+ */
+static int validate_the_platform(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx_nfc_platform_data *pdata = pdev->dev.platform_data;
+
+ /* Validate the clock name. */
+
+ if (!pdata->clock_name) {
+ dev_err(dev, "No clock name\n");
+ return -ENXIO;
+ }
+
+ /* Validate the partitions. */
+
+ if ((pdata->partitions && (!pdata->partition_count)) ||
+ (!pdata->partitions && (pdata->partition_count))) {
+ dev_err(dev, "Bad partition data\n");
+ return -ENXIO;
+ }
+
+ /* Return success */
+
+ return 0;
+
+}
+
+/**
+ * set_up_the_nfc_hal() - Sets up for the specific NFC hardware HAL.
+ *
+ * @this: Per-device data.
+ */
+static int set_up_the_nfc_hal(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ struct nfc_hal *p;
+ struct imx_nfc_platform_data *pdata = this->pdata;
+
+ for (i = 0; i < ARRAY_SIZE(nfc_hals); i++) {
+
+ p = nfc_hals[i];
+
+ /*
+ * Restrict to 3.2 until others are fully implemented.
+ *
+ * TODO: Remove this.
+ */
+
+ if ((p->major_version != 3) && (p->minor_version != 2))
+ continue;
+
+ if ((p->major_version == pdata->nfc_major_version) &&
+ (p->minor_version == pdata->nfc_minor_version)) {
+ this->nfc = p;
+ return 0;
+ break;
+ }
+
+ }
+
+ dev_err(this->dev, "Unkown NFC version %d.%d\n",
+ pdata->nfc_major_version, pdata->nfc_minor_version);
+
+ return !0;
+
+}
+
+/**
+ * acquire_resources() - Tries to acquire resources.
+ *
+ * @this: Per-device data.
+ */
+static int acquire_resources(struct imx_nfc_data *this)
+{
+
+ int error = 0;
+ struct platform_device *pdev = this->pdev;
+ struct device *dev = this->dev;
+ struct imx_nfc_platform_data *pdata = this->pdata;
+ struct resource *r;
+
+ /* Find the buffers and map them. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_BUFFERS_ADDR_RES_NAME);
+
+ if (!r) {
+ dev_err(dev, "Can't get '%s'\n", IMX_NFC_BUFFERS_ADDR_RES_NAME);
+ error = -ENXIO;
+ goto exit_buffers;
+ }
+
+ this->buffers = ioremap(r->start, r->end - r->start + 1);
+
+ if (!this->buffers) {
+ dev_err(dev, "Can't remap buffers\n");
+ error = -EIO;
+ goto exit_buffers;
+ }
+
+ /* Find the primary registers and map them. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);
+
+ if (!r) {
+ dev_err(dev, "Can't get '%s'\n",
+ IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);
+ error = -ENXIO;
+ goto exit_primary_registers;
+ }
+
+ this->primary_regs = ioremap(r->start, r->end - r->start + 1);
+
+ if (!this->primary_regs) {
+ dev_err(dev, "Can't remap the primary registers\n");
+ error = -EIO;
+ goto exit_primary_registers;
+ }
+
+ /* Check for secondary registers. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
+
+ if (r && !this->nfc->has_secondary_regs) {
+
+ dev_err(dev, "Resource '%s' should not be present\n",
+ IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
+ error = -ENXIO;
+ goto exit_secondary_registers;
+
+ }
+
+ if (this->nfc->has_secondary_regs) {
+
+ if (!r) {
+ dev_err(dev, "Can't get '%s'\n",
+ IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
+ error = -ENXIO;
+ goto exit_secondary_registers;
+ }
+
+ this->secondary_regs = ioremap(r->start, r->end - r->start + 1);
+
+ if (!this->secondary_regs) {
+ dev_err(dev,
+ "Can't remap the secondary registers\n");
+ error = -EIO;
+ goto exit_secondary_registers;
+ }
+
+ }
+
+ /* Find out what our interrupt is and try to own it. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ IMX_NFC_INTERRUPT_RES_NAME);
+
+ if (!r) {
+ dev_err(dev, "Can't get '%s'\n", IMX_NFC_INTERRUPT_RES_NAME);
+ error = -ENXIO;
+ goto exit_irq;
+ }
+
+ this->interrupt = r->start;
+
+ error = request_irq(this->interrupt,
+ nfc_util_isr, 0, this->dev->bus_id, this);
+
+ if (error) {
+ dev_err(dev, "Can't own interrupt %d\n", this->interrupt);
+ goto exit_irq;
+ }
+
+ /* Find out the name of our clock and try to own it. */
+
+ this->clock = clk_get(dev, pdata->clock_name);
+
+ if (this->clock == ERR_PTR(-ENOENT)) {
+ dev_err(dev, "Can't get clock '%s'\n", pdata->clock_name);
+ error = -ENXIO;
+ goto exit_clock;
+ }
+
+ /* Return success. */
+
+ return 0;
+
+ /* Error return paths begin here. */
+
+exit_clock:
+ free_irq(this->interrupt, this);
+exit_irq:
+ if (this->secondary_regs)
+ iounmap(this->secondary_regs);
+exit_secondary_registers:
+ iounmap(this->primary_regs);
+exit_primary_registers:
+ iounmap(this->buffers);
+exit_buffers:
+ return error;
+
+}
+
+/**
+ * release_resources() - Releases resources.
+ *
+ * @this: Per-device data.
+ */
+static void release_resources(struct imx_nfc_data *this)
+{
+
+ /* Release our clock. */
+
+ clk_disable(this->clock);
+ clk_put(this->clock);
+
+ /* Release our interrupt. */
+
+ free_irq(this->interrupt, this);
+
+ /* Release mapped memory. */
+
+ iounmap(this->buffers);
+ iounmap(this->primary_regs);
+ if (this->secondary_regs)
+ iounmap(this->secondary_regs);
+
+}
+
+/**
+ * register_with_mtd() - Registers this medium with MTD.
+ *
+ * @this: Per-device data.
+ */
+static int register_with_mtd(struct imx_nfc_data *this)
+{
+ int error = 0;
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *nand = &this->nand;
+ struct device *dev = this->dev;
+ struct imx_nfc_platform_data *pdata = this->pdata;
+
+ /* Link the MTD structures together, along with our own data. */
+
+ mtd->priv = nand;
+ nand->priv = this;
+
+ /* Prepare the MTD structure. */
+
+ mtd->owner = THIS_MODULE;
+
+ /*
+ * Signal Control Functions
+ */
+
+ nand->cmd_ctrl = mil_cmd_ctrl;
+
+ /*
+ * Chip Control Functions
+ *
+ * Not all of our NFC hardware versions expose Ready/Busy signals. For
+ * versions that don't, the is_ready function pointer will be NULL. In
+ * those cases, we leave the dev_ready member unassigned, which will
+ * cause the HIL to use a reference implementation's algorithm to
+ * discover when the hardware is ready.
+ */
+
+ nand->select_chip = mil_select_chip;
+ nand->cmdfunc = mil_cmdfunc;
+ nand->waitfunc = mil_waitfunc;
+
+ if (this->nfc->is_ready)
+ nand->dev_ready = mil_dev_ready;
+
+ /*
+ * Low-level I/O Functions
+ */
+
+ nand->read_byte = mil_read_byte;
+ nand->read_word = mil_read_word;
+ nand->read_buf = mil_read_buf;
+ nand->write_buf = mil_write_buf;
+ nand->verify_buf = mil_verify_buf;
+
+ /*
+ * ECC Control Functions
+ */
+
+ nand->ecc.hwctl = mil_ecc_hwctl;
+ nand->ecc.calculate = mil_ecc_calculate;
+ nand->ecc.correct = mil_ecc_correct;
+
+ /*
+ * ECC-Aware I/O Functions
+ */
+
+ nand->ecc.read_page = mil_ecc_read_page;
+ nand->ecc.read_page_raw = mil_ecc_read_page_raw;
+ nand->ecc.write_page = mil_ecc_write_page;
+ nand->ecc.write_page_raw = mil_ecc_write_page_raw;
+
+ /*
+ * High-level I/O Functions
+ */
+
+ nand->write_page = mil_write_page;
+ nand->ecc.read_oob = mil_ecc_read_oob;
+ nand->ecc.write_oob = mil_ecc_write_oob;
+
+ /*
+ * Bad Block Marking Functions
+ *
+ * We want to use the reference block_bad and block_markbad
+ * implementations, so we don't assign those members.
+ */
+
+ nand->scan_bbt = mil_scan_bbt;
+
+ /*
+ * Error Recovery Functions
+ *
+ * We don't fill in the errstat function pointer because it's optional
+ * and we don't have a need for it.
+ */
+
+ /*
+ * Device Attributes
+ *
+ * We don't fill in the chip_delay member because we don't have a need
+ * for it.
+ *
+ * We support only 8-bit Flash bus width.
+ */
+
+ /*
+ * ECC Attributes
+ *
+ * Members that aren't set here are configured by a version-specific
+ * set_geometry() function.
+ */
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.size = NFC_MAIN_BUF_SIZE;
+ nand->ecc.prepad = 0;
+ nand->ecc.postpad = 0;
+
+ /*
+ * Bad Block Management Attributes
+ *
+ * We don't fill in the following attributes:
+ *
+ * badblockpos
+ * bbt
+ * badblock_pattern
+ * bbt_erase_shift
+ *
+ * These attributes aren't hardware-specific, and the HIL makes fine
+ * choices without our help.
+ */
+
+ nand->options |= NAND_USE_FLASH_BBT;
+ nand->bbt_td = &bbt_main_descriptor;
+ nand->bbt_md = &bbt_mirror_descriptor;
+
+ /*
+ * Device Control
+ *
+ * We don't fill in the controller attribute. In principle, we could set
+ * up a structure to represent the controller. However, since it's
+ * vanishingly improbable that we'll have more than one medium behind
+ * the controller, it's not worth the effort. We let the HIL handle it.
+ */
+
+ /*
+ * Memory-mapped I/O
+ *
+ * We don't fill in the following attributes:
+ *
+ * IO_ADDR_R
+ * IO_ADDR_W
+ *
+ * None of these are necessary because we don't have a memory-mapped
+ * implementation.
+ */
+
+ /*
+ * Install a "fake" ECC layout.
+ *
+ * We'll be calling nand_scan() to do the final MTD setup. If we haven't
+ * already chosen an ECC layout, then nand_scan() will choose one based
+ * on the part geometry it discovers. Unfortunately, it doesn't make
+ * good choices. It would be best if we could install the correct ECC
+ * layout now, before we call nand_scan(). We can't do that because we
+ * don't know the medium geometry yet. Here, we install a "fake" ECC
+ * layout just to stop nand_scan() from trying to pick on for itself.
+ * Later, in imx_nfc_scan_bbt(), when we know the medium geometry, we'll
+ * install the correct choice.
+ *
+ * Of course, this tactic depends critically on nand_scan() not using
+ * the fake layout before we can install a good one. This is in fact the
+ * case.
+ */
+
+ nand->ecc.layout = &nfc_geometry_512_16_RS_ECC1.mtd_layout;
+
+ /*
+ * Ask the NAND Flash system to scan for chips.
+ *
+ * This will fill in reference implementations for all the members of
+ * the MTD structures that we didn't set, and will make the medium fully
+ * usable.
+ */
+
+ error = nand_scan(mtd, this->nfc->max_chip_count);
+
+ if (error) {
+ dev_err(dev, "Chip scan failed\n");
+ error = -ENXIO;
+ goto exit_scan;
+ }
+
+ /* Register the MTD that represents the entire medium. */
+
+ mtd->name = "NAND Flash Medium";
+
+ add_mtd_device(mtd);
+
+ /* Check if we're doing partitions and register MTD's accordingly. */
+
+ #ifdef CONFIG_MTD_PARTITIONS
+
+ /*
+ * Look for partition information. If we find some, install
+ * them. Otherwise, use the partitions handed to us by the
+ * platform.
+ */
+
+ this->partition_count =
+ parse_mtd_partitions(mtd, partition_source_types,
+ &this->partitions, 0);
+
+ if ((this->partition_count <= 0) && (pdata->partitions)) {
+ this->partition_count = pdata->partition_count;
+ this->partitions = pdata->partitions;
+ }
+
+ if (this->partitions)
+ add_mtd_partitions(mtd, this->partitions,
+ this->partition_count);
+
+ #endif
+
+ /* Return success. */
+
+ return 0;
+
+ /* Error return paths begin here. */
+
+exit_scan:
+ return error;
+
+}
+
+/**
+ * unregister_with_mtd() - Unregisters this medium with MTD.
+ *
+ * @this: Per-device data.
+ */
+static void unregister_with_mtd(struct imx_nfc_data *this)
+{
+
+ /* Get MTD to let go. */
+
+ nand_release(&this->mtd);
+
+}
+
+/**
+ * manage_sysfs_files() - Creates/removes sysfs files for this device.
+ *
+ * @this: Per-device data.
+ */
+static void manage_sysfs_files(struct imx_nfc_data *this, int create)
+{
+ int error;
+ unsigned int i;
+ struct device_attribute **attr;
+
+ for (i = 0, attr = device_attributes;
+ i < ARRAY_SIZE(device_attributes); i++, attr++) {
+
+ if (create) {
+ error = device_create_file(this->dev, *attr);
+ if (error) {
+ while (--attr >= device_attributes)
+ device_remove_file(this->dev, *attr);
+ return;
+ }
+ } else {
+ device_remove_file(this->dev, *attr);
+ }
+
+ }
+
+}
+
+/**
+ * imx_nfc_probe() - Probes for a device and, if possible, takes ownership.
+ *
+ * @pdev: A pointer to the platform device.
+ */
+static int imx_nfc_probe(struct platform_device *pdev)
+{
+ int error = 0;
+ char *symmetric_clock;
+ struct clk *parent_clock;
+ unsigned long parent_clock_rate_in_hz;
+ unsigned long parent_clock_rate_in_mhz;
+ unsigned long nfc_clock_rate_in_hz;
+ unsigned long nfc_clock_rate_in_mhz;
+ unsigned long clock_divisor;
+ unsigned long cycle_in_ns;
+ struct device *dev = &pdev->dev;
+ struct imx_nfc_platform_data *pdata = pdev->dev.platform_data;
+ struct imx_nfc_data *this = 0;
+
+ /* Say hello. */
+
+ dev_info(dev, "Probing...\n");
+
+ /* Check if we're enabled. */
+
+ if (!imx_nfc_module_enable) {
+ dev_info(dev, "Disabled\n");
+ return -ENXIO;
+ }
+
+ /* Validate the platform device data. */
+
+ error = validate_the_platform(pdev);
+
+ if (error)
+ goto exit_validate_platform;
+
+ /* Allocate memory for the per-device data. */
+
+ this = kzalloc(sizeof(*this), GFP_KERNEL);
+
+ if (!this) {
+ dev_err(dev, "Failed to allocate per-device memory\n");
+ error = -ENOMEM;
+ goto exit_allocate_this;
+ }
+
+ /* Link our per-device data to the owning device. */
+
+ platform_set_drvdata(pdev, this);
+
+ /* Fill in the convenience pointers in our per-device data. */
+
+ this->pdev = pdev;
+ this->dev = &pdev->dev;
+ this->pdata = pdata;
+
+ /* Initialize the interrupt service pathway. */
+
+ init_completion(&this->done);
+
+ /* Set up the NFC HAL. */
+
+ error = set_up_the_nfc_hal(this);
+
+ if (error)
+ goto exit_set_up_nfc_hal;
+
+ /* Attempt to acquire the resources we need. */
+
+ error = acquire_resources(this);
+
+ if (error)
+ goto exit_acquire_resources;
+
+ /* Initialize the NFC HAL. */
+
+ if (this->nfc->init(this)) {
+ error = -ENXIO;
+ goto exit_nfc_init;
+ }
+
+ /* Tell the platform we're bringing this device up. */
+
+ if (pdata->init)
+ error = pdata->init();
+
+ if (error)
+ goto exit_platform_init;
+
+ /* Report. */
+
+ parent_clock = clk_get_parent(this->clock);
+ parent_clock_rate_in_hz = clk_get_rate(parent_clock);
+ parent_clock_rate_in_mhz = parent_clock_rate_in_hz / 1000000;
+ nfc_clock_rate_in_hz = clk_get_rate(this->clock);
+ nfc_clock_rate_in_mhz = nfc_clock_rate_in_hz / 1000000;
+
+ clock_divisor = parent_clock_rate_in_hz / nfc_clock_rate_in_hz;
+ symmetric_clock = this->nfc->get_symmetric(this) ? "Yes" : "No";
+ cycle_in_ns = get_cycle_in_ns(this);
+
+ dev_dbg(dev, "-------------\n");
+ dev_dbg(dev, "Configuration\n");
+ dev_dbg(dev, "-------------\n");
+ dev_dbg(dev, "NFC Version : %d.%d\n" , this->nfc->major_version,
+ this->nfc->minor_version);
+ dev_dbg(dev, "Buffers : 0x%p\n" , this->buffers);
+ dev_dbg(dev, "Primary Regs : 0x%p\n" , this->primary_regs);
+ dev_dbg(dev, "Secondary Regs : 0x%p\n" , this->secondary_regs);
+ dev_dbg(dev, "Interrupt : %u\n" , this->interrupt);
+ dev_dbg(dev, "Clock Name : %s\n" , pdata->clock_name);
+ dev_dbg(dev, "Parent Clock Rate: %lu Hz (%lu MHz)\n",
+ parent_clock_rate_in_hz,
+ parent_clock_rate_in_mhz);
+ dev_dbg(dev, "Clock Divisor : %lu\n", clock_divisor);
+ dev_dbg(dev, "NFC Clock Rate : %lu Hz (%lu MHz)\n",
+ nfc_clock_rate_in_hz,
+ nfc_clock_rate_in_mhz);
+ dev_dbg(dev, "Symmetric Clock : %s\n" , symmetric_clock);
+ dev_dbg(dev, "Actual Cycle : %lu ns\n" , cycle_in_ns);
+ dev_dbg(dev, "Target Cycle : %u ns\n" , pdata->target_cycle_in_ns);
+
+ /* Initialize the Medium Abstraction Layer. */
+
+ mal_init(this);
+
+ /* Initialize the MTD Interface Layer. */
+
+ mil_init(this);
+
+ /* Register this medium with MTD. */
+
+ error = register_with_mtd(this);
+
+ if (error)
+ goto exit_mtd_registration;
+
+ /* Create sysfs entries for this device. */
+
+ manage_sysfs_files(this, true);
+
+ /* Return success. */
+
+ return 0;
+
+ /* Error return paths begin here. */
+
+exit_mtd_registration:
+ if (pdata->exit)
+ pdata->exit();
+exit_platform_init:
+ this->nfc->exit(this);
+exit_nfc_init:
+exit_acquire_resources:
+exit_set_up_nfc_hal:
+ platform_set_drvdata(pdev, NULL);
+ kfree(this);
+exit_allocate_this:
+exit_validate_platform:
+ return error;
+
+}
+
+/**
+ * imx_nfc_remove() - Dissociates this driver from the given device.
+ *
+ * @pdev: A pointer to the device.
+ */
+static int __exit imx_nfc_remove(struct platform_device *pdev)
+{
+ struct imx_nfc_data *this = platform_get_drvdata(pdev);
+
+ /* Remove sysfs entries for this device. */
+
+ manage_sysfs_files(this, false);
+
+ /* Unregister with the NAND Flash MTD system. */
+
+ unregister_with_mtd(this);
+
+ /* Tell the platform we're shutting down this device. */
+
+ if (this->pdata->exit)
+ this->pdata->exit();
+
+ /* Shut down the NFC. */
+
+ this->nfc->exit(this);
+
+ /* Release our resources. */
+
+ release_resources(this);
+
+ /* Unlink our per-device data from the platform device. */
+
+ platform_set_drvdata(pdev, NULL);
+
+ /* Free our per-device data. */
+
+ kfree(this);
+
+ /* Return success. */
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/**
+ * suspend() - Puts the NFC in a low power state.
+ *
+ * Refer to Documentation/driver-model/driver.txt for more information.
+ *
+ * @pdev: A pointer to the device.
+ * @state: The new power state.
+ */
+
+static int imx_nfc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int error = 0;
+ struct imx_nfc_data *this = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = &this->mtd;
+ struct device *dev = &this->pdev->dev;
+
+ dev_dbg(dev, "Suspending...\n");
+
+ /* Suspend MTD's use of this device. */
+
+ error = mtd->suspend(mtd);
+
+ /* Suspend the actual hardware. */
+
+ clk_disable(this->clock);
+
+ return error;
+
+}
+
+/**
+ * resume() - Brings the NFC back from a low power state.
+ *
+ * Refer to Documentation/driver-model/driver.txt for more information.
+ *
+ * @pdev: A pointer to the device.
+ */
+static int imx_nfc_resume(struct platform_device *pdev)
+{
+ struct imx_nfc_data *this = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = &this->mtd;
+ struct device *dev = &this->pdev->dev;
+
+ dev_dbg(dev, "Resuming...\n");
+
+ /* Resume MTD's use of this device. */
+
+ mtd->resume(mtd);
+
+ return 0;
+
+}
+
+#else
+
+#define suspend NULL
+#define resume NULL
+
+#endif /* CONFIG_PM */
+
+/*
+ * This structure represents this driver to the platform management system.
+ */
+static struct platform_driver imx_nfc_driver = {
+ .driver = {
+ .name = IMX_NFC_DRIVER_NAME,
+ },
+ .probe = imx_nfc_probe,
+ .remove = __exit_p(imx_nfc_remove),
+ .suspend = imx_nfc_suspend,
+ .resume = imx_nfc_resume,
+};
+
+/**
+ * imx_nfc_init() - Initializes this module.
+ */
+static int __init imx_nfc_init(void)
+{
+
+ pr_info("i.MX NFC driver %s\n", DRIVER_VERSION);
+
+ /* Register this driver with the platform management system. */
+
+ if (platform_driver_register(&imx_nfc_driver) != 0) {
+ pr_err("i.MX NFC driver registration failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+
+}
+
+/**
+ * imx_nfc_exit() - Deactivates this module.
+ */
+static void __exit imx_nfc_exit(void)
+{
+ pr_debug("i.MX NFC driver exiting...\n");
+ platform_driver_unregister(&imx_nfc_driver);
+}
+
+module_init(imx_nfc_init);
+module_exit(imx_nfc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX NAND Flash Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/lba/Makefile b/drivers/mtd/nand/lba/Makefile
new file mode 100644
index 000000000000..0e576bfa856d
--- /dev/null
+++ b/drivers/mtd/nand/lba/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MTD_NAND_GPMI_LBA) += gpmi_lba.o
+gpmi_lba-objs += gpmi-transport.o lba-core.o lba-blk.o
diff --git a/drivers/mtd/nand/lba/gpmi-transport.c b/drivers/mtd/nand/lba/gpmi-transport.c
new file mode 100644
index 000000000000..0073842db6b9
--- /dev/null
+++ b/drivers/mtd/nand/lba/gpmi-transport.c
@@ -0,0 +1,832 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI transport layer for LBA driver
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ * Clock settings and hw init comes from
+ * gpmi driver by Dmitry Pervushin.
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/ctype.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <asm/div64.h>
+#include <mach/platform.h>
+#include <mach/regs-apbh.h>
+#include <mach/regs-gpmi.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+#include "lba.h"
+
+struct lba_data *g_data;
+static int max_chips = 1;
+static long clk = -1;
+
+struct gpmi_nand_timing gpmi_safe_timing = {
+ .address_setup = 25,
+ .data_setup = 80,
+ .data_hold = 60,
+ .dsample_time = 6,
+};
+
+/******************************************************************************
+ * HW init part
+ ******************************************************************************/
+
+/**
+ * gpmi_irq - IRQ handler
+ *
+ * @irq: irq no
+ * @context: IRQ context, pointer to gpmi_nand_data
+ */
+static irqreturn_t gpmi_irq(int irq, void *context)
+{
+ struct lba_data *data = context;
+ int i;
+
+ for (i = 0; i < max_chips; i++) {
+ if (stmp3xxx_dma_is_interrupt(data->nand[i].dma_ch)) {
+ stmp3xxx_dma_clear_interrupt(data->nand[i].dma_ch);
+ complete(&data->nand[i].done);
+ }
+
+ }
+ stmp3xxx_clearl(BM_GPMI_CTRL1_DEV_IRQ | BM_GPMI_CTRL1_TIMEOUT_IRQ,
+ REGS_GPMI_BASE + HW_GPMI_CTRL1);
+ return IRQ_HANDLED;
+}
+
+static inline u32 gpmi_cycles_ceil(u32 ntime, u32 period)
+{
+ int k;
+
+ k = (ntime + period - 1) / period;
+ if (k == 0)
+ k++;
+ return k ;
+}
+
+
+/**
+ * gpmi_set_timings - set GPMI timings
+ * @pdev: pointer to GPMI platform device
+ * @tm: pointer to structure &gpmi_nand_timing with new timings
+ *
+ * During initialization, GPMI uses safe sub-optimal timings, which
+ * can be changed after reading boot control blocks
+ */
+void gpmi_set_timings(struct lba_data *data, struct gpmi_nand_timing *tm)
+{
+ u32 period_ns = 1000000 / clk_get_rate(data->clk) + 1;
+ u32 address_cycles, data_setup_cycles;
+ u32 data_hold_cycles, data_sample_cycles;
+ u32 busy_timeout;
+ u32 t0, reg;
+
+ address_cycles = gpmi_cycles_ceil(tm->address_setup, period_ns);
+ data_setup_cycles = gpmi_cycles_ceil(tm->data_setup, period_ns);
+ data_hold_cycles = gpmi_cycles_ceil(tm->data_hold, period_ns);
+ data_sample_cycles = gpmi_cycles_ceil(tm->dsample_time + period_ns / 4,
+ period_ns / 2);
+ busy_timeout = gpmi_cycles_ceil(10000000 / 4096, period_ns);
+
+ t0 = address_cycles << BP_GPMI_TIMING0_ADDRESS_SETUP;
+ t0 |= data_setup_cycles << BP_GPMI_TIMING0_DATA_SETUP;
+ t0 |= data_hold_cycles << BP_GPMI_TIMING0_DATA_HOLD;
+ __raw_writel(t0, REGS_GPMI_BASE + HW_GPMI_TIMING0);
+
+ __raw_writel(busy_timeout, REGS_GPMI_BASE + HW_GPMI_TIMING1);
+
+ reg = __raw_readl(REGS_GPMI_BASE + HW_GPMI_CTRL1);
+#ifdef CONFIG_ARCH_STMP378X
+ reg &= ~BM_GPMI_CTRL1_RDN_DELAY;
+ reg |= data_sample_cycles << BP_GPMI_CTRL1_RDN_DELAY;
+#else
+ reg &= ~BM_GPMI_CTRL1_DSAMPLE_TIME;
+ reg |= data_sample_cycles << BP_GPMI_CTRL1_DSAMPLE_TIME;
+#endif
+ __raw_writel(reg, REGS_GPMI_BASE + HW_GPMI_CTRL1);
+}
+
+void queue_plug(struct lba_data *data)
+{
+ u32 ctrl1;
+ clk_enable(data->clk);
+ if (clk <= 0)
+ clk = 24000; /* safe setting, some chips do not work on
+ speeds >= 24kHz */
+ clk_set_rate(data->clk, clk);
+
+ clk = clk_get_rate(data->clk);
+
+
+ stmp3xxx_reset_block(HW_GPMI_CTRL0 + REGS_GPMI_BASE, 1);
+
+ ctrl1 = __raw_readl(REGS_GPMI_BASE + HW_GPMI_CTRL1);
+
+ /* write protection OFF */
+ ctrl1 |= BM_GPMI_CTRL1_DEV_RESET;
+
+ /* IRQ polarity */
+ ctrl1 |= BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY;
+
+ /* ...and ECC module */
+ /*HW_GPMI_CTRL1_SET(bch_mode());*/
+
+ /* choose NAND mode (1 means ATA, 0 - NAND */
+ ctrl1 &= ~BM_GPMI_CTRL1_GPMI_MODE;
+
+ __raw_writel(ctrl1, REGS_GPMI_BASE + HW_GPMI_CTRL1);
+
+ gpmi_set_timings(data, &gpmi_safe_timing);
+}
+
+void queue_release(struct lba_data *data)
+{
+ stmp3xxx_setl(BM_GPMI_CTRL0_SFTRST, REGS_GPMI_BASE + HW_GPMI_CTRL0);
+
+ clk_disable(data->clk);
+}
+
+
+/**
+ * gpmi_init_hw - initialize the hardware
+ * @pdev: pointer to platform device
+ *
+ * Initialize GPMI hardware and set default (safe) timings for NAND access.
+ * Returns error code or 0 on success
+ */
+static int gpmi_init_hw(struct platform_device *pdev, int request_pins)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+ struct gpmi_platform_data *gpd =
+ (struct gpmi_platform_data *)pdev->dev.platform_data;
+ int err = 0;
+
+ data->clk = clk_get(NULL, "gpmi");
+ if (IS_ERR(data->clk)) {
+ err = PTR_ERR(data->clk);
+ dev_err(&pdev->dev, "cannot set failsafe clockrate\n");
+ goto out;
+ }
+ if (request_pins)
+ gpd->pinmux(1);
+
+ queue_plug(data);
+
+out:
+ return err;
+}
+/**
+ * gpmi_release_hw - free the hardware
+ * @pdev: pointer to platform device
+ *
+ * In opposite to gpmi_init_hw, release all acquired resources
+ */
+static void gpmi_release_hw(struct platform_device *pdev)
+{
+ struct gpmi_platform_data *gpd =
+ (struct gpmi_platform_data *)pdev->dev.platform_data;
+ struct lba_data *data = platform_get_drvdata(pdev);
+
+ queue_release(data);
+ clk_put(data->clk);
+ gpd->pinmux(0);
+}
+
+
+/**
+ * gpmi_alloc_buffers - allocate DMA buffers for one chip
+ *
+ * @pdev: GPMI platform device
+ * @g: pointer to structure associated with NAND chip
+ *
+ * Allocate buffer using dma_alloc_coherent
+ */
+static int gpmi_alloc_buffers(struct platform_device *pdev,
+ struct gpmi_perchip_data *g)
+{
+ g->cmd_buffer = dma_alloc_coherent(&pdev->dev,
+ g->cmd_buffer_size,
+ &g->cmd_buffer_handle, GFP_DMA);
+ if (!g->cmd_buffer)
+ goto out1;
+
+ g->write_buffer = dma_alloc_coherent(&pdev->dev,
+ g->write_buffer_size,
+ &g->write_buffer_handle, GFP_DMA);
+ if (!g->write_buffer)
+ goto out2;
+
+ g->data_buffer = dma_alloc_coherent(&pdev->dev,
+ g->data_buffer_size,
+ &g->data_buffer_handle, GFP_DMA);
+ if (!g->data_buffer)
+ goto out3;
+
+ g->oob_buffer = dma_alloc_coherent(&pdev->dev,
+ g->oob_buffer_size,
+ &g->oob_buffer_handle, GFP_DMA);
+ if (!g->oob_buffer)
+ goto out4;
+
+ g->cmdtail_buffer = dma_alloc_coherent(&pdev->dev,
+ g->cmdtail_buffer_size,
+ &g->cmdtail_buffer_handle, GFP_DMA);
+ if (!g->oob_buffer)
+ goto out5;
+
+ return 0;
+
+out5:
+ dma_free_coherent(&pdev->dev, g->oob_buffer_size,
+ g->oob_buffer, g->oob_buffer_handle);
+
+out4:
+ dma_free_coherent(&pdev->dev, g->data_buffer_size,
+ g->data_buffer, g->data_buffer_handle);
+out3:
+ dma_free_coherent(&pdev->dev, g->write_buffer_size,
+ g->write_buffer, g->write_buffer_handle);
+out2:
+ dma_free_coherent(&pdev->dev, g->cmd_buffer_size,
+ g->cmd_buffer, g->cmd_buffer_handle);
+out1:
+ return -ENOMEM;
+}
+
+/**
+ * gpmi_free_buffers - free buffers allocated by gpmi_alloc_buffers
+ *
+ * @pdev: platform device
+ * @g: pointer to structure associated with NAND chip
+ *
+ * Deallocate buffers on exit
+ */
+static void gpmi_free_buffers(struct platform_device *pdev,
+ struct gpmi_perchip_data *g)
+{
+ dma_free_coherent(&pdev->dev, g->oob_buffer_size,
+ g->oob_buffer, g->oob_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->write_buffer_size,
+ g->write_buffer, g->write_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->cmd_buffer_size,
+ g->cmd_buffer, g->cmd_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->cmdtail_buffer_size,
+ g->cmdtail_buffer, g->cmdtail_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->data_buffer_size,
+ g->data_buffer, g->data_buffer_handle);
+}
+
+
+/******************************************************************************
+ * Arch specific chain_* callbaks
+ ******************************************************************************/
+
+/**
+ * chain_w4r - Initialize descriptor to perform W4R operation
+ *
+ * @chain: Descriptor to use
+ * @cs: CS for this operation
+ *
+ * Due to HW bug we have to put W4R into separate desc.
+ */
+static void chain_w4r(struct stmp3xxx_dma_descriptor *chain, int cs)
+{
+ chain->command->cmd =
+ (4 << BP_APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ (BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER << BP_APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ (BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY << BP_GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ (BV_GPMI_CTRL0_ADDRESS__NAND_DATA << BP_GPMI_CTRL0_ADDRESS) |
+ (cs << BP_GPMI_CTRL0_CS);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = 0;
+}
+
+/**
+ * chain_cmd - Initialize descriptor to push CMD to the bus
+ *
+ * @chain: Descriptor to use
+ * @cmd_handle: dma_addr_t pointer that holds the command
+ * @lba_cmd: flags and lenghth of this command.
+ * @cs: CS for this operation
+ *
+ * CLE || CLE+ALE
+ */
+static void chain_cmd(struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t cmd_handle,
+ struct lba_cmd *lba_cmd,
+ int cs)
+{
+ /* output command */
+ chain->command->cmd =
+ (lba_cmd->len << BP_APBH_CHn_CMD_XFER_COUNT) |
+ (3 << BP_APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ (BV_APBH_CHn_CMD_COMMAND__DMA_READ << BP_APBH_CHn_CMD_COMMAND);
+ chain->command->cmd |= BM_APBH_CHn_CMD_CHAIN;
+ chain->command->pio_words[0] =
+ (BV_GPMI_CTRL0_COMMAND_MODE__WRITE << BP_GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ (cs << BP_GPMI_CTRL0_CS) |
+ (BV_GPMI_CTRL0_ADDRESS__NAND_CLE << BP_GPMI_CTRL0_ADDRESS) |
+ (lba_cmd->len << BP_GPMI_CTRL0_XFER_COUNT);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->buf_ptr = cmd_handle;
+
+ if (lba_cmd->flag & FE_CMD_INC)
+ chain->command->pio_words[0] |= BM_GPMI_CTRL0_ADDRESS_INCREMENT;
+/*BUG if (lba_cmd->flag & FE_W4R) */
+/* chain->command->cmd |= BM_APBH_CHn_CMD_NANDWAIT4READY; */
+}
+
+/**
+ * chain_cmd - Initialize descriptor to read data from the bus
+ *
+ * @chain: Descriptor to use
+ * @data_handle: dma_addr_t pointer to buffer to store data
+ * @data_len: the size of the data buffer to read
+ * @cmd_handle: dma_addr_t pointer that holds the command
+ * @lba_cmd: flags and lenghth of this command.
+ * @cs: CS for this operation
+ */
+static void chain_read_data(struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t data_handle,
+ dma_addr_t data_len,
+ struct lba_cmd *lba_cmd,
+ int cs)
+{
+ chain->command->cmd =
+ (data_len << BP_APBH_CHn_CMD_XFER_COUNT) |
+ (4 << BP_APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ (BV_APBH_CHn_CMD_COMMAND__DMA_WRITE << BP_APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ (BV_GPMI_CTRL0_COMMAND_MODE__READ << BP_GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ (cs << BP_GPMI_CTRL0_CS) |
+ (BV_GPMI_CTRL0_ADDRESS__NAND_DATA << BP_GPMI_CTRL0_ADDRESS) |
+ (data_len << BP_GPMI_CTRL0_XFER_COUNT);
+
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = data_handle;
+
+ if (lba_cmd->flag & FE_CMD_INC)
+ chain->command->pio_words[0] |= BM_GPMI_CTRL0_ADDRESS_INCREMENT;
+/*BUG if (lba_cmd->flag & FE_W4R) */
+/* chain->command->cmd |= BM_APBH_CHn_CMD_NANDWAIT4READY; */
+
+}
+
+/**
+ * chain_cmd - Initialize descriptor to read data from the bus
+ *
+ * @chain: Descriptor to use
+ * @data_handle: dma_addr_t pointer to buffer to read data from
+ * @data_len: the size of the data buffer to write
+ * @cmd_handle: dma_addr_t pointer that holds the command
+ * @lba_cmd: flags and lenghth of this command.
+ * @cs: CS for this operation
+ */
+static void chain_write_data(struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t data_handle,
+ int data_len,
+ struct lba_cmd *lba_cmd,
+ int cs)
+{
+
+ chain->command->cmd =
+ (data_len << BP_APBH_CHn_CMD_XFER_COUNT) |
+ (4 << BP_APBH_CHn_CMD_CMDWORDS) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ (BV_APBH_CHn_CMD_COMMAND__DMA_READ << BP_APBH_CHn_CMD_COMMAND);
+ chain->command->pio_words[0] =
+ (BV_GPMI_CTRL0_COMMAND_MODE__WRITE << BP_GPMI_CTRL0_COMMAND_MODE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ (cs << BP_GPMI_CTRL0_CS) |
+ (BV_GPMI_CTRL0_ADDRESS__NAND_DATA << BP_GPMI_CTRL0_ADDRESS) |
+ (data_len << BP_GPMI_CTRL0_XFER_COUNT);
+
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = data_handle;
+
+ if (lba_cmd->flag & FE_CMD_INC)
+ chain->command->pio_words[0] |= BM_GPMI_CTRL0_ADDRESS_INCREMENT;
+/*BUG if (lba_cmd->flag & FE_W4R) */
+/* chain->command->cmd |= BM_APBH_CHn_CMD_NANDWAIT4READY; */
+
+}
+
+
+/******************************************************************************
+ * Interface to arch independent part
+ ******************************************************************************/
+/**
+ * queue_cmd - Setup a chain of descriptors
+ *
+ * @priv: private data passed
+ * @cmd_buf: pointer to command buffer (to be removed)
+ * @cmd_handle: dma_addr_t pointer that holds the command
+ * @cmd_len: the size of the command buffer (to be removed)
+ * @data_handle: dma_addr_t pointer to a data buffer
+ * @data_len: the size of the data buffer
+ * @cmd_flags: commands flags
+ */
+int queue_cmd(void *priv,
+ uint8_t *cmd_buf, dma_addr_t cmd_handle, int cmd_len,
+ dma_addr_t data, int data_len,
+ struct lba_cmd *cmd_flags)
+{
+
+ struct gpmi_perchip_data *g = priv;
+ unsigned long flags;
+ int idx;
+ int ret = 0;
+ struct stmp3xxx_dma_descriptor *chain ;
+ int i;
+
+ if (!g || !(cmd_buf || cmd_handle))
+ BUG();
+
+ spin_lock_irqsave(&g->lock, flags);
+
+ /* Keep it for debug purpose */
+ chain = g->d;
+ for (i = g->d_tail; i < GPMI_DMA_MAX_CHAIN; i++) {
+ chain[i].command->cmd = 0;
+ chain[i].command->buf_ptr = 0;
+ }
+ /* End */
+
+ if (!cmd_handle) {
+ if (!cmd_buf)
+ BUG();
+ memcpy(g->cmd_buffer, cmd_buf, cmd_len);
+ cmd_handle = g->cmd_buffer_handle;
+ }
+
+ idx = g->d_tail;
+ chain = &g->d[idx];
+
+ do {
+ if (!cmd_flags)
+ BUG();
+
+ if (cmd_flags->flag & FE_W4R) {
+ /* there seems to be HW BUG with W4R flag.
+ * IRQ controller hangs forever when it's combined
+ * with real operation.
+ */
+ chain_w4r(chain, g->cs);
+ chain++; idx++;
+ }
+
+
+ switch (cmd_flags->flag & F_MASK) {
+
+ case F_CMD:
+ chain_cmd(chain, cmd_handle, cmd_flags, g->cs);
+ break;
+ case F_DATA_READ:
+ chain_read_data(chain, data, data_len,
+ cmd_flags, g->cs);
+ break;
+ case F_DATA_WRITE:
+ chain_write_data(chain, data, data_len,
+ cmd_flags, g->cs);
+ break;
+ default:{
+ if (cmd_flags->flag & FE_END)
+ goto out;
+ else{
+ printk(KERN_ERR "uknown cmd\n");
+ BUG();
+ }
+ }
+ }
+
+
+ chain++; idx++;
+ cmd_handle += cmd_flags->len;
+
+ if (idx >= GPMI_DMA_MAX_CHAIN) {
+ printk(KERN_ERR "to many chains; idx is 0x%x\n", idx);
+ BUG();
+ }
+
+ } while (!((cmd_flags++)->flag & FE_END));
+
+out:
+ if (idx < GPMI_DMA_MAX_CHAIN) {
+ ret = idx;
+ g->d_tail = idx;
+ }
+ spin_unlock_irqrestore(g->lock, flags);
+
+ return ret;
+
+}
+
+dma_addr_t queue_get_cmd_handle(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+ return g->cmd_buffer_handle;
+}
+
+uint8_t *queue_get_cmd_ptr(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+ return g->cmd_buffer;
+}
+
+dma_addr_t queue_get_data_handle(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+ return g->data_buffer_handle;
+}
+
+uint8_t *queue_get_data_ptr(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+ return g->data_buffer;
+}
+
+
+/**
+ * queue_run - run the chain
+ *
+ * @priv: private data.
+ */
+int queue_run(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+
+ if (!g->d_tail)
+ return 0;
+ stmp3xxx_dma_reset_channel(g->dma_ch);
+ stmp3xxx_dma_clear_interrupt(g->dma_ch);
+ stmp3xxx_dma_enable_interrupt(g->dma_ch);
+
+ g->d[g->d_tail-1].command->cmd &= ~(BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN);
+ g->d[g->d_tail-1].command->cmd |= BM_APBH_CHn_CMD_IRQONCMPLT ;
+
+ g->d[g->d_tail-1].command->pio_words[0] &= ~BM_GPMI_CTRL0_LOCK_CS;
+
+#ifdef DEBUG
+ /*stmp37cc_dma_print_chain(&g->chain);*/
+#endif
+
+ init_completion(&g->done);
+ stmp3xxx_dma_go(g->dma_ch, g->d, 1);
+ wait_for_completion(&g->done);
+
+ g->d_tail = 0;
+
+ return 0;
+
+}
+
+/******************************************************************************
+ * Platform specific part / chard driver and misc functions
+ ******************************************************************************/
+
+
+
+static int __init lba_probe(struct platform_device *pdev)
+{
+ struct lba_data *data;
+ struct resource *r;
+ struct gpmi_perchip_data *g;
+ int err;
+
+ /* Allocate memory for the device structure (and zero it) */
+ data = kzalloc(sizeof(*data) + sizeof(struct gpmi_perchip_data),
+ GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "failed to allocate gpmi_nand_data\n");
+ err = -ENOMEM;
+ goto out1;
+ }
+ g_data = data;
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get resource\n");
+ err = -ENXIO;
+ goto out2;
+ }
+ data->io_base = ioremap(r->start, r->end - r->start + 1);
+ if (!data->io_base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ err = -EIO;
+ goto out2;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ err = -EIO;
+ dev_err(&pdev->dev, "can't get IRQ resource\n");
+ goto out3;
+ }
+ data->irq = r->start;
+
+ platform_set_drvdata(pdev, data);
+ err = gpmi_init_hw(pdev, 1);
+ if (err)
+ goto out3;
+
+
+ err = request_irq(data->irq,
+ gpmi_irq, 0, dev_name(&pdev->dev), data);
+ if (err) {
+ dev_err(&pdev->dev, "can't request GPMI IRQ\n");
+ goto out4;
+ }
+
+ g = data->nand;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get DMA resource\n");
+ goto out_res;
+ }
+ g->cs = 0;
+ g->dma_ch = r->start;
+
+ err = stmp3xxx_dma_request(g->dma_ch, NULL, dev_name(&pdev->dev));
+ if (err) {
+ dev_err(&pdev->dev, "can't request DMA channel 0x%x\n",
+ g->dma_ch);
+ goto out_res;
+ }
+
+ err = stmp3xxx_dma_make_chain(g->dma_ch, &g->chain,
+ g->d, ARRAY_SIZE(g->d));
+ if (err) {
+ dev_err(&pdev->dev, "can't setup DMA chain\n");
+ stmp3xxx_dma_release(g->dma_ch);
+ goto out_res;
+ }
+
+ g->cmd_buffer_size = GPMI_CMD_BUF_SZ;
+ g->cmdtail_buffer_size = GPMI_CMD_BUF_SZ;
+ g->write_buffer_size = GPMI_WRITE_BUF_SZ;
+ g->data_buffer_size = GPMI_DATA_BUF_SZ;
+ g->oob_buffer_size = GPMI_OOB_BUF_SZ;
+
+ err = gpmi_alloc_buffers(pdev, g);
+ if (err) {
+ dev_err(&pdev->dev, "can't setup buffers\n");
+ stmp3xxx_dma_free_chain(&g->chain);
+ stmp3xxx_dma_release(g->dma_ch);
+ goto out_res;
+ }
+
+ g->dev = pdev;
+ g->chip.priv = g;
+ g->index = 0;
+ g->timing = gpmi_safe_timing;
+
+ g->cmd_buffer_sz =
+ g->write_buffer_sz =
+ g->data_buffer_sz =
+ 0;
+ g->valid = !0; /* mark the data as valid */
+
+
+ lba_core_init(data);
+
+ return 0;
+
+out_res:
+ free_irq(data->irq, data);
+out4:
+ gpmi_release_hw(pdev);
+out3:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(data->io_base);
+out2:
+ kfree(data);
+out1:
+ return err;
+}
+
+static int gpmi_suspend(struct platform_device *pdev, pm_message_t pm)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+ int err;
+
+ printk(KERN_INFO "%s: %d\n", __func__, __LINE__);
+ err = lba_core_suspend(pdev, data);
+ if (!err)
+ gpmi_release_hw(pdev);
+
+ return err;
+}
+
+static int gpmi_resume(struct platform_device *pdev)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+ int r;
+
+ printk(KERN_INFO "%s: %d\n", __func__, __LINE__);
+ r = gpmi_init_hw(pdev, 1);
+ lba_core_resume(pdev, data);
+ return r;
+}
+
+/**
+ * gpmi_nand_remove - remove a GPMI device
+ *
+ */
+static int __devexit lba_remove(struct platform_device *pdev)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+ int i;
+
+ lba_core_remove(data);
+ gpmi_release_hw(pdev);
+ free_irq(data->irq, data);
+
+ for (i = 0; i < max_chips; i++) {
+ if (!data->nand[i].valid)
+ continue;
+ gpmi_free_buffers(pdev, &data->nand[i]);
+ stmp3xxx_dma_free_chain(&data->nand[i].chain);
+ stmp3xxx_dma_release(data->nand[i].dma_ch);
+ }
+ iounmap(data->io_base);
+ kfree(data);
+
+ return 0;
+}
+
+static struct platform_driver lba_driver = {
+ .probe = lba_probe,
+ .remove = __devexit_p(lba_remove),
+ .driver = {
+ .name = "gpmi",
+ .owner = THIS_MODULE,
+ },
+ .suspend = gpmi_suspend,
+ .resume = gpmi_resume,
+
+};
+
+
+static int __init lba_init(void)
+{
+ return platform_driver_register(&lba_driver);
+}
+
+static void lba_exit(void)
+{
+ platform_driver_unregister(&lba_driver);
+}
+
+module_init(lba_init);
+module_exit(lba_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/lba/gpmi.h b/drivers/mtd/nand/lba/gpmi.h
new file mode 100644
index 000000000000..a647c948040f
--- /dev/null
+++ b/drivers/mtd/nand/lba/gpmi.h
@@ -0,0 +1,103 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __DRIVERS_GPMI_H
+#define __DRIVERS_GPMI_H
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+
+#include <mach/gpmi.h>
+#include <mach/regs-gpmi.h>
+#include <mach/regs-apbh.h>
+#ifdef CONFIG_MTD_NAND_GPMI_BCH
+#include <mach/regs-bch.h>
+#endif
+
+
+struct gpmi_nand_timing {
+ u8 data_setup;
+ u8 data_hold;
+ u8 address_setup;
+ u8 dsample_time;
+};
+
+#define GPMI_DMA_MAX_CHAIN 20 /* max DMA commands in chain */
+
+#define GPMI_CMD_BUF_SZ 10
+#define GPMI_DATA_BUF_SZ 4096
+#define GPMI_WRITE_BUF_SZ 4096
+#define GPMI_OOB_BUF_SZ 218
+
+
+struct gpmi_perchip_data {
+ int valid;
+ struct nand_chip chip;
+ struct platform_device *dev;
+ int index;
+
+ spinlock_t lock; /* protect chain operations */
+ struct stmp37xx_circ_dma_chain chain;
+ struct stmp3xxx_dma_descriptor d[GPMI_DMA_MAX_CHAIN];
+ int d_tail;
+
+ struct completion done;
+
+ u8 *cmd_buffer;
+ dma_addr_t cmd_buffer_handle;
+ int cmd_buffer_size, cmd_buffer_sz;
+
+ u8 *cmdtail_buffer;
+ dma_addr_t cmdtail_buffer_handle;
+ int cmdtail_buffer_size, cmdtail_buffer_sz;
+
+ u8 *write_buffer;
+ dma_addr_t write_buffer_handle;
+ int write_buffer_size, write_buffer_sz;
+
+ u8 *data_buffer;
+ dma_addr_t data_buffer_handle;
+ u8 *data_buffer_cptr;
+ int data_buffer_size, data_buffer_sz, bytes2read;
+
+ u8 *oob_buffer;
+ dma_addr_t oob_buffer_handle;
+ int oob_buffer_size;
+
+ int cs;
+ unsigned dma_ch;
+
+ int ecc_oob_bytes, oob_free;
+
+ struct gpmi_nand_timing timing;
+
+ void *p2w, *oob2w, *p2r, *oob2r;
+ size_t p2w_size, oob2w_size, p2r_size, oob2r_size;
+ dma_addr_t p2w_dma, oob2w_dma, p2r_dma, oob2r_dma;
+ unsigned read_memcpy:1, write_memcpy:1,
+ read_oob_memcpy:1, write_oob_memcpy:1;
+};
+
+
+extern struct gpmi_nand_timing gpmi_safe_timing;
+
+
+#endif
diff --git a/drivers/mtd/nand/lba/lba-blk.c b/drivers/mtd/nand/lba/lba-blk.c
new file mode 100644
index 000000000000..228aa9d27760
--- /dev/null
+++ b/drivers/mtd/nand/lba/lba-blk.c
@@ -0,0 +1,345 @@
+/*
+ * Freescale STMP37XX/STMP378X LBA/block driver
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h> /* kmalloc() */
+#include <linux/fs.h> /* everything... */
+#include <linux/errno.h> /* error codes */
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/types.h> /* size_t */
+#include <linux/fcntl.h> /* O_ACCMODE */
+#include <linux/hdreg.h> /* HDIO_GETGEO */
+#include <linux/kdev_t.h>
+#include <linux/vmalloc.h>
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h> /* invalidate_bdev */
+#include <linux/bio.h>
+#include <linux/dma-mapping.h>
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include "lba.h"
+
+static int lba_major;
+
+#define LBA_NAME "lba"
+
+#if 0
+#define TAG() printk(KERNE_ERR "%s: %d\n", __func__, __LINE__)
+#else
+#define TAG()
+#endif
+
+/*
+ * The internal representation of our device.
+ */
+struct lba_blk_dev {
+ int size; /* Device size in sectors */
+ spinlock_t lock; /* For mutual exclusion */
+ int users;
+ struct request_queue *queue; /* The device request queue */
+ struct gendisk *gd; /* The gendisk structure */
+ struct lba_data *data; /* pointer from lba core */
+
+ struct task_struct *thread;
+ struct bio *bio_head;
+ struct bio *bio_tail;
+ wait_queue_head_t wait_q;
+ struct semaphore busy;
+
+};
+
+static struct lba_blk_dev *g_lba_blk;
+
+static void blk_add_bio(struct lba_blk_dev *dev, struct bio *bio);
+
+
+/*
+ * Transfer a single BIO.
+ */
+static int lba_blk_xfer_bio(struct lba_blk_dev *dev, struct bio *bio)
+{
+ int i;
+ struct bio_vec *bvec;
+ sector_t sector = bio->bi_sector;
+ enum dma_data_direction dir;
+ int status = 0;
+ int (*lba_xfer)(void *priv,
+ unsigned int sector,
+ unsigned int count,
+ void *buffer,
+ dma_addr_t handle);
+
+ if (bio_data_dir(bio) == WRITE) {
+ lba_xfer = lba_write_sectors;
+ dir = DMA_TO_DEVICE;
+ } else {
+ lba_xfer = lba_read_sectors;
+ dir = DMA_FROM_DEVICE;
+ }
+
+ /* Fixme: merge segments */
+ bio_for_each_segment(bvec, bio, i) {
+ void *buffer = page_address(bvec->bv_page);
+ dma_addr_t handle ;
+ if (!buffer)
+ BUG();
+ buffer += bvec->bv_offset;
+ handle = dma_map_single(&dev->data->nand->dev->dev,
+ buffer,
+ bvec->bv_len,
+ dir);
+ status = lba_xfer(dev->data->nand, sector,
+ bvec->bv_len >> 9,
+ buffer,
+ handle);
+
+ dma_unmap_single(&dev->data->nand->dev->dev,
+ handle,
+ bvec->bv_len,
+ dir);
+ if (status)
+ break;
+
+ sector += bio_cur_bytes(bio) >> 9;
+ }
+
+ return status;
+}
+
+
+/*
+ * The direct make request version.
+ */
+static int lba_make_request(struct request_queue *q, struct bio *bio)
+{
+ struct lba_blk_dev *dev = q->queuedata;
+
+ blk_add_bio(dev, bio);
+ return 0;
+}
+
+/*
+ * Open and close.
+ */
+
+static int lba_blk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct lba_blk_dev *dev = bdev->bd_disk->private_data;
+
+ TAG();
+
+ spin_lock_irq(&dev->lock);
+ dev->users++;
+ spin_unlock_irq(&dev->lock);
+ return 0;
+}
+
+
+static int lba_blk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct lba_blk_dev *dev = gd->private_data;
+
+ spin_lock(&dev->lock);
+ dev->users--;
+ spin_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int lba_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+ /*
+ * get geometry: we have to fake one... trim the size to a
+ * multiple of 2048 (1M): tell we have 32 sectors, 64 heads,
+ * whatever cylinders.
+ */
+ geo->heads = 1 << 6;
+ geo->sectors = 1 << 5;
+ geo->cylinders = get_capacity(bdev->bd_disk) >> 11;
+ return 0;
+}
+
+/*
+ * Add bio to back of pending list
+ */
+static void blk_add_bio(struct lba_blk_dev *dev, struct bio *bio)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->bio_tail) {
+ dev->bio_tail->bi_next = bio;
+ dev->bio_tail = bio;
+ } else
+ dev->bio_head = dev->bio_tail = bio;
+ wake_up(&dev->wait_q);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * Grab first pending buffer
+ */
+static struct bio *blk_get_bio(struct lba_blk_dev *dev)
+{
+ struct bio *bio;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ bio = dev->bio_head;
+ if (bio) {
+ if (bio == dev->bio_tail) {
+ dev->bio_tail = NULL;
+ dev->bio_head = NULL;
+ }
+ dev->bio_head = bio->bi_next;
+ bio->bi_next = NULL;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return bio;
+}
+
+static int lba_thread(void *data)
+{
+ struct lba_blk_dev *dev = data;
+ struct bio *bio;
+ int status;
+
+ set_user_nice(current, -20);
+
+ while (!kthread_should_stop() || dev->bio_head) {
+
+ wait_event_interruptible(dev->wait_q,
+ dev->bio_head || kthread_should_stop());
+
+ if (!dev->bio_head)
+ continue;
+
+ if (lba_core_lock_mode(dev->data, LBA_MODE_MDP))
+ continue;
+
+ bio = blk_get_bio(dev);
+ status = lba_blk_xfer_bio(dev, bio);
+ bio_endio(bio, status);
+
+ lba_core_unlock_mode(dev->data);
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * The device operations structure.
+ */
+static struct block_device_operations lba_blk_ops = {
+ .owner = THIS_MODULE,
+ .open = lba_blk_open,
+ .release = lba_blk_release,
+ .getgeo = lba_getgeo,
+};
+
+
+int lba_blk_init(struct lba_data *data)
+{
+
+ struct lba_blk_dev *dev;
+ int err;
+ if (!data)
+ BUG();
+
+ printk(KERN_INFO "LBA block driver v0.1\n");
+ lba_major = LBA_MAJOR;
+ dev = g_lba_blk = kzalloc(sizeof(struct lba_blk_dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->data = data;
+ register_blkdev(lba_major, "lba");
+
+ spin_lock_init(&dev->lock);
+ init_waitqueue_head(&dev->wait_q);
+ sema_init(&dev->busy, 1);
+
+ dev->queue = blk_alloc_queue(GFP_KERNEL);
+ if (!dev->queue)
+ goto out2;
+ blk_queue_make_request(dev->queue, lba_make_request);
+ /*dev->queue->unplug_fn = lba_unplug_device;*/
+ blk_queue_logical_block_size(dev->queue, 512);
+
+ dev->queue->queuedata = dev;
+ dev->gd = alloc_disk(32);
+ if (!dev->gd) {
+ printk(KERN_ERR "failed to alloc disk\n");
+ goto out3;
+ }
+ dev->size = data->mdp_size ;
+ printk(KERN_INFO "%s: set capacity of the device to 0x%x\n",
+ __func__, dev->size);
+ dev->gd->major = lba_major;
+ dev->gd->first_minor = 0;
+ dev->gd->fops = &lba_blk_ops;
+ dev->gd->queue = dev->queue;
+ dev->gd->private_data = dev;
+ snprintf(dev->gd->disk_name, 8, LBA_NAME);
+ set_capacity(dev->gd, dev->size);
+
+ dev->thread = kthread_create(lba_thread, dev, "lba-%d", 1);
+ if (IS_ERR(dev->thread)) {
+ err = PTR_ERR(dev->thread);
+ goto out3;
+ }
+ wake_up_process(dev->thread);
+
+ add_disk(dev->gd);
+
+
+ TAG();
+
+ return 0;
+out3:
+out2:
+ unregister_blkdev(lba_major, "lba");
+ return -ENOMEM;
+}
+
+int lba_blk_remove(struct lba_data *data)
+{
+
+ struct lba_blk_dev *dev = g_lba_blk;
+
+ del_gendisk(dev->gd);
+ kthread_stop(dev->thread);
+ blk_cleanup_queue(dev->queue);
+ put_disk(dev->gd);
+
+ unregister_blkdev(lba_major, LBA_NAME);
+ kfree(dev);
+ return 0;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_BLOCKDEV_MAJOR(254);
diff --git a/drivers/mtd/nand/lba/lba-core.c b/drivers/mtd/nand/lba/lba-core.c
new file mode 100644
index 000000000000..cdf28ca42b2a
--- /dev/null
+++ b/drivers/mtd/nand/lba/lba-core.c
@@ -0,0 +1,619 @@
+/*
+ * Freescale STMP37XX/STMP378X LBA/core driver
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/dma-mapping.h>
+#include <linux/ctype.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+#include "lba.h"
+
+#define LBA_SELFPM_TIMEOUT 2000 /* msecs */
+dma_addr_t g_cmd_handle;
+dma_addr_t g_data_handle;
+uint8_t *g_data_buffer;
+uint8_t *g_cmd_buffer;
+
+uint8_t lba_get_status1(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x70 } ;
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_W4R},
+ {0, F_DATA_READ | FE_END},
+ };
+ *g_data_buffer = 0;
+ queue_cmd(priv, cmd_buf, 0, 1, g_data_handle, 1, lba_flags);
+ queue_run(priv);
+ return *g_data_buffer;
+}
+
+
+int lba_wait_for_ready(void *priv)
+{
+ int stat;
+ unsigned long j_start = jiffies;
+
+ stat = lba_get_status1(priv);
+ if ((stat & 0x60) != 0x60) {
+ while (((stat & 0x60) != 0x60) &&
+ (jiffies - j_start < msecs_to_jiffies(2000))) {
+ schedule();
+ stat = lba_get_status1(priv);
+ }
+ }
+ if (stat != 0x60)
+ return stat;
+
+ return 0;
+}
+
+int lba_write_sectors(void *priv, unsigned int sector, unsigned int count,
+ void *buffer, dma_addr_t handle)
+{
+ uint8_t cmd_buf[] = {
+ 0x80,
+ count & 0xff, (count >> 8) & 0xff, /* Count */
+ (sector & 0xff), (sector >> 8) & 0xff, /* Address */
+ (sector >> 16) & 0xff, (sector >> 24) & 0xff, /* Addres */
+ /* Data goes here */
+ 0x10
+ };
+
+ struct lba_cmd flags_t1[] = { /* Transmition mode 1/A */
+ {7 , F_CMD | FE_CMD_INC | FE_W4R},
+ {0, F_DATA_WRITE},
+ {1 , F_CMD | FE_END}
+ };
+
+ if (count > 8)
+ return -EINVAL;
+
+ if (lba_wait_for_ready(priv))
+ return -EIO;
+
+ while (count) {
+ int cnt = (count < 8) ? count : 8;
+ int data_len = cnt * 512;
+
+ queue_cmd(priv, cmd_buf, 0, 8,
+ handle, data_len, flags_t1);
+
+ handle += data_len;
+ count -= cnt;
+
+ }
+
+ queue_run(priv);
+
+ return count;
+
+}
+
+int lba_read_sectors(void *priv, unsigned int sector, unsigned int count,
+ void *buffer, dma_addr_t handle)
+{
+
+ int data_len;
+ int cnt;
+ uint8_t cmd_buf[] = {
+ 0x00,
+ count & 0xff, (count >> 8) & 0xff, /* Count */
+ (sector & 0xff), (sector >> 8) & 0xff, /* Addr */
+ (sector >> 16) & 0xff, (sector >> 24) & 0xff, /* Addr */
+ 0x30
+ /* Data goes here <data> */
+
+ };
+
+ struct lba_cmd flags_r3[] = { /* Read mode 3/A */
+ {7 , F_CMD | FE_CMD_INC | FE_W4R},
+ {1 , F_CMD },
+ {0 , F_DATA_READ | FE_W4R | FE_END },
+ };
+ struct lba_cmd flags_r3c[] = { /* Read mode 3/A */
+ {0 , F_DATA_READ | FE_W4R | FE_END },
+ };
+ struct lba_cmd *flags = flags_r3;
+ int flags_len = 8;
+
+ if (count > 8)
+ return -EINVAL;
+
+ if (lba_wait_for_ready(priv))
+ return -EIO;
+
+ while (count) {
+ cnt = (count < 8) ? count : 8;
+ data_len = cnt * 512;
+ queue_cmd(priv, cmd_buf, 0, flags_len, handle, data_len, flags);
+ handle += data_len;
+ count -= cnt;
+ flags = flags_r3c;
+ flags_len = 0;
+ }
+
+ queue_run(priv);
+
+ return count;
+
+}
+
+
+uint8_t lba_get_id1(void *priv, uint8_t *ret_buffer)
+{
+ uint8_t cmd_buf[] = { 0x90 , 0x00, /* Data read 5bytes*/ };
+ struct lba_cmd lba_flags[] = {
+ {2 , F_CMD | FE_CMD_INC | FE_W4R},
+ {0, F_DATA_READ | FE_END},
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 2, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+ memcpy(ret_buffer, g_data_buffer, 5);
+
+ return 0;
+}
+
+uint8_t lba_get_id2(void *priv, uint8_t *ret_buffer)
+{
+ uint8_t cmd_buf[] = { 0x92 , 0x00, /* Data read 5bytes*/ };
+ struct lba_cmd lba_flags[] = {
+ {2 , F_CMD | FE_CMD_INC | FE_W4R},
+ {0, F_DATA_READ | FE_END},
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 2, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+ memcpy(ret_buffer, g_data_buffer, 5);
+ return 0;
+}
+
+uint8_t lba_get_status2(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x71 };
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_CMD_INC | FE_W4R},
+ {0 , F_DATA_READ | FE_END},
+ };
+ *g_data_buffer = 0;
+ queue_cmd(priv, cmd_buf, 0, 1, g_data_handle, 1, lba_flags);
+ queue_run(priv);
+ return *g_data_buffer;
+}
+
+static uint8_t lba_parse_status2(void *priv)
+{
+ uint8_t stat;
+
+ stat = lba_get_status2(priv);
+ printk(KERN_INFO "Status2:|");
+ if (stat & 0x40)
+ printk(" C.PAR.ERR |"); /* no KERN_ here */
+ if (stat & 0x20)
+ printk(" NO spare |");
+ if (stat & 0x10)
+ printk(" ADDR OoRange |");
+ if (stat & 0x8)
+ printk(" high speed |");
+ if ((stat & 0x6) == 6)
+ printk(" MDP |");
+ if ((stat & 0x6) == 4)
+ printk(" VFP |");
+ if ((stat & 0x6) == 2)
+ printk(" PNP |");
+ if (stat & 1)
+ printk(" PSW |");
+
+ printk("\n");
+ return 0;
+}
+
+
+int lba_2mdp(void *priv)
+{
+ uint8_t cmd_buf[] = { 0xFC };
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_W4R | FE_END}
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 1, 0, 0, lba_flags);
+ queue_run(priv);
+ return 0;
+}
+
+void _lba_misc_cmd_set(void *priv, uint8_t *cmd_buf)
+{
+ struct lba_cmd lba_flags[] = {
+ {6 , F_CMD | FE_CMD_INC | FE_W4R },
+ {1 , F_CMD | FE_END },
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 7, 0, 0, lba_flags);
+ queue_run(priv);
+}
+
+uint8_t _lba_misc_cmd_get(void *priv, uint8_t *cmd_buf)
+{
+ struct lba_cmd lba_flags[] = {
+ {6 , F_CMD | FE_CMD_INC | FE_W4R },
+ {1 , F_CMD },
+ {0 , F_DATA_READ | FE_W4R | FE_END}
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 7, g_data_handle, 1, lba_flags);
+ queue_run(priv);
+ return *g_data_buffer;
+}
+void lba_mdp2vfp(void *priv, uint8_t pass[2])
+{
+ uint8_t cmd_buf[] = { 0x0, 0xbe, pass[0], pass[1], 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_bcm2vfp(void *priv, uint8_t pass[2])
+{
+ lba_mdp2vfp(priv, pass);
+}
+
+void lba_powersave_enable(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xba, 0, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+void lba_powersave_disable(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xbb, 0, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_highspeed_enable(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xbc, 0, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_highspeed_disable(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xbd, 0, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_prot1_set(void *priv, uint8_t mode)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xa2, mode, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_prot2_set(void *priv, uint8_t mode)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xa3, mode, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+uint8_t lba_prot1_get(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xb2, 0, 0, 0, 0, 0x57 };
+ return _lba_misc_cmd_get(priv, cmd_buf);
+}
+
+uint8_t lba_prot2_get(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xb3, 0, 0, 0, 0, 0x57 };
+ return _lba_misc_cmd_get(priv, cmd_buf);
+}
+
+uint64_t lba_mdp_size_get(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xb0, 0, 0, 0, 0, 0x57 };
+ struct lba_cmd lba_flags[] = {
+ {6 , F_CMD | FE_CMD_INC | FE_W4R },
+ {1 , F_CMD },
+ {0 , F_DATA_READ | FE_W4R | FE_END}
+ };
+
+ memset((void *)g_data_buffer, 0, 8);
+ queue_cmd(priv, cmd_buf, 0, 7, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+ return le64_to_cpu(*(long long *)g_data_buffer);
+}
+
+void lba_cache_flush(void *priv)
+{
+ uint8_t cmd_buf[] = { 0xF9 };
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_W4R },
+ {0 , FE_W4R | FE_END}
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 7, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+}
+
+void lba_reboot(void *priv)
+{
+ uint8_t cmd_buf[] = { 0xFD };
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_W4R },
+ {0 , FE_W4R | FE_END}
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 7, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+}
+
+void lba_def_state(void *priv)
+{
+ lba_wait_for_ready(priv);
+ lba_reboot(priv);
+
+ lba_wait_for_ready(priv);
+ lba_parse_status2(priv);
+
+ lba_wait_for_ready(priv);
+ lba_2mdp(priv);
+
+ lba_wait_for_ready(priv);
+ lba_prot1_set(priv, LBA_T_SIZE8); /* 512 * 8 */
+
+ lba_wait_for_ready(priv);
+/* Type C read; Type A write; */
+ lba_prot2_set(priv, LBA_P_WRITE_A | LBA_P_READ_C);
+}
+
+/*
+ * Should be called with mode locked
+ */
+void lba_core_setvfp_passwd(struct lba_data *data, uint8_t pass[2])
+{
+ memcpy(data->pass, pass, 2);
+}
+
+int lba_core_lock_mode(struct lba_data *data, int mode)
+{
+ void *priv = &data->nand;
+
+ if (down_interruptible(&data->mode_lock))
+ return -EAGAIN;
+ /*
+ * MDP and VFP are the only supported
+ * modes for now.
+ */
+ if ((mode != LBA_MODE_MDP) &&
+ (mode != LBA_MODE_VFP)) {
+ up(&data->mode_lock);
+ return -EINVAL;
+ }
+
+ while ((data->mode & LBA_MODE_MASK) == LBA_MODE_SUSP) {
+ up(&data->mode_lock);
+
+ if (wait_event_interruptible(
+ data->suspend_q,
+ (data->mode & LBA_MODE_MASK) != LBA_MODE_SUSP))
+ return -EAGAIN;
+
+ if (down_interruptible(&data->mode_lock))
+ return -EAGAIN;
+
+ data->last_access = jiffies;
+ }
+
+ if (data->mode & LBA_MODE_SELFPM) {
+ queue_plug(data);
+ data->mode &= ~LBA_MODE_SELFPM;
+ }
+
+ if (mode == data->mode)
+ return 0;
+
+ /*
+ * mode = VFP || MDP only
+ * Revisit when more modes are added
+ */
+ switch (data->mode) {
+ case LBA_MODE_RST:
+ case LBA_MODE_PNR:
+ case LBA_MODE_BCM:
+ lba_def_state(priv);
+ if (mode == LBA_MODE_MDP) {
+ data->mode = LBA_MODE_MDP;
+ break;
+ }
+ /*no break -> fall down to set VFP mode*/
+ case LBA_MODE_MDP:
+ lba_wait_for_ready(priv);
+ lba_mdp2vfp(priv, data->pass);
+ data->mode = LBA_MODE_VFP;
+ break;
+ case LBA_MODE_VFP:
+ lba_wait_for_ready(priv);
+ lba_2mdp(priv);
+ data->mode = LBA_MODE_MDP;
+ break;
+ default:
+ up(&data->mode_lock);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int lba_core_unlock_mode(struct lba_data *data)
+{
+ data->last_access = jiffies;
+ up(&data->mode_lock);
+ wake_up(&data->selfpm_q);
+ return 0;
+}
+
+static int selfpm_timeout_expired(struct lba_data *data)
+{
+ return jiffies_to_msecs(jiffies - data->last_access) > 2000;
+}
+
+static int lba_selfpm_thread(void *d)
+{
+ struct lba_data *data = d;
+
+ set_user_nice(current, -5);
+
+ while (!kthread_should_stop()) {
+
+ if (wait_event_interruptible(data->selfpm_q,
+ kthread_should_stop() ||
+ !(data->mode & LBA_MODE_SELFPM)))
+ continue;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(LBA_SELFPM_TIMEOUT));
+
+ if (down_trylock(&data->mode_lock))
+ continue;
+
+ if (!selfpm_timeout_expired(data)) {
+ up(&data->mode_lock);
+ continue;
+ }
+ data->mode |= LBA_MODE_SELFPM;
+ lba_wait_for_ready((void *)data->nand);
+ lba_cache_flush((void *)data->nand);
+ queue_release(data);
+ up(&data->mode_lock);
+
+ }
+
+ return 0;
+}
+
+int lba_core_init(struct lba_data *data)
+{
+ uint8_t id_buf[5];
+ uint8_t capacity;
+ uint8_t id1_template[5] = {0x98, 0xDC, 0x00, 0x15, 0x00};
+ uint8_t id2_template[5] = {0x98, 0x21, 0x00, 0x55, 0xAA};
+ void *priv = (void *)data->nand;
+
+
+ g_data = data;
+ g_cmd_handle = queue_get_cmd_handle(priv);
+ g_data_handle = queue_get_data_handle(priv);
+ g_data_buffer = queue_get_data_ptr(priv);
+ g_cmd_buffer = queue_get_cmd_ptr(priv);
+
+ spin_lock_init(&data->lock);
+ sema_init(&data->mode_lock, 1);
+ init_waitqueue_head(&data->suspend_q);
+ init_waitqueue_head(&data->selfpm_q);
+
+
+ lba_get_id1(data->nand, id_buf);
+ if (!memcmp(id_buf, id1_template, 5))
+ printk(KERN_INFO
+ "LBA: Found LBA/SLC NAND emulated ID\n");
+ else
+ return -ENODEV;
+
+ lba_get_id2(data->nand, id_buf);
+ capacity = id_buf[2];
+ id_buf[2] = 0;
+
+ if (memcmp(id_buf, id2_template, 5)) {
+ printk(KERN_INFO
+ "LBA: Uknown LBA device\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO
+ "LBA: Found %dGbytes LBA NAND device\n",
+ 1 << capacity);
+
+ lba_wait_for_ready(priv);
+ lba_parse_status2(priv);
+
+ lba_def_state(priv);
+ data->mode = LBA_MODE_MDP;
+
+ g_data->pnp_size = 0xff;
+ g_data->vfp_size = 16384;
+
+ lba_wait_for_ready(priv);
+ g_data->mdp_size = lba_mdp_size_get(priv);
+
+ lba_wait_for_ready(priv);
+ /*lba_powersave_enable(priv);*/
+ /*lba_highspeed_enable(priv);*/
+
+ lba_wait_for_ready(priv);
+ lba_parse_status2(priv);
+
+ data->thread = kthread_create(lba_selfpm_thread,
+ data, "lba-selfpm-%d", 1);
+ if (IS_ERR(data->thread))
+ return PTR_ERR(data->thread);
+
+ lba_blk_init(g_data);
+
+ wake_up_process(data->thread);
+ return 0;
+
+};
+
+int lba_core_remove(struct lba_data *data)
+{
+ kthread_stop(data->thread);
+ lba_blk_remove(data);
+ lba_wait_for_ready((void *)data->nand);
+ lba_cache_flush((void *)data->nand);
+ return 0;
+}
+
+int lba_core_suspend(struct platform_device *pdev, struct lba_data *data)
+{
+ BUG_ON((data->mode & 0xffff) == LBA_MODE_SUSP);
+ if (down_interruptible(&data->mode_lock))
+ return -EAGAIN;
+ if (data->mode & LBA_MODE_SELFPM)
+ queue_plug(data);
+
+ data->mode = LBA_MODE_SUSP | LBA_MODE_SELFPM;
+ up(&data->mode_lock);
+ lba_wait_for_ready((void *)data->nand);
+ lba_cache_flush((void *)data->nand);
+ return 0;
+}
+
+int lba_core_resume(struct platform_device *pdev, struct lba_data *data)
+{
+ BUG_ON((data->mode & 0xffff) != LBA_MODE_SUSP);
+ lba_def_state((void *)data->nand);
+ data->last_access = jiffies;
+ data->mode = LBA_MODE_MDP;
+ wake_up(&data->suspend_q);
+ return 0;
+}
diff --git a/drivers/mtd/nand/lba/lba.h b/drivers/mtd/nand/lba/lba.h
new file mode 100644
index 000000000000..3466f96881eb
--- /dev/null
+++ b/drivers/mtd/nand/lba/lba.h
@@ -0,0 +1,140 @@
+/*
+ * Freescale STMP37XX/STMP378X LBA interface
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __INCLUDE_LBA_H__
+#define __INCLUDE_LBA_H__
+
+
+#include <linux/spinlock.h>
+#include <linux/kthread.h>
+#include "gpmi.h"
+
+struct lba_cmd {
+ uint8_t len;
+#define F_MASK 0x0f
+#define F_ALE 0x01
+#define F_CMD 0x02
+#define F_DATA_READ 0x04
+#define F_DATA_WRITE 0x08
+
+#define FE_W4R 0x10
+#define FE_CMD_INC 0x20
+#define FE_END 0x40
+
+ uint8_t flag;
+};
+
+#define LBA_P_READ_A 0
+#define LBA_P_READ_B 2
+#define LBA_P_READ_C 3
+#define LBA_P_WRITE_A 0
+#define LBA_P_WRITE_B 4
+#define LBA_T_SIZE1 1
+#define LBA_T_SIZE4 2
+#define LBA_T_SIZE8 4
+#define LBA_T_CRC (1 << 6)
+#define LBA_T_ECC_CHECK (2 << 6)
+#define LBA_T_ECC_CORRECT (3 << 6)
+
+struct lba_data {
+ void __iomem *io_base;
+ struct clk *clk;
+ int irq;
+
+ spinlock_t lock;
+ int use_count;
+ int mode;
+ struct semaphore mode_lock;
+#define LBA_MODE_MASK 0x0000ffff
+#define LBA_FLAG_MASK 0xffff0000
+#define LBA_MODE_RST 0
+#define LBA_MODE_PNR 1
+#define LBA_MODE_BCM 2
+#define LBA_MODE_MDP 3
+#define LBA_MODE_VFP 4
+#define LBA_MODE_SUSP 5
+#define LBA_MODE_SELFPM 0x80000000
+ wait_queue_head_t suspend_q;
+ wait_queue_head_t selfpm_q;
+ struct task_struct *thread;
+ long long last_access;
+ /* PNR specific */
+ /* BCM specific */
+ /* VFP specific */
+ uint8_t pass[2];
+
+ /* Size of the partiotions: pages for PNP; sectors for others */
+ unsigned int pnp_size;
+ unsigned int vfp_size;
+ long long mdp_size;
+ void *priv;
+ /*should be last*/
+ struct gpmi_perchip_data nand[0];
+
+};
+
+extern struct lba_data *g_data;
+
+void stmp37cc_dma_print_chain(struct stmp37xx_circ_dma_chain *chain);
+
+int lba_blk_init(struct lba_data *data);
+int lba_blk_remove(struct lba_data *data);
+int lba_blk_suspend(struct platform_device *pdev, struct lba_data *data);
+int lba_blk_resume(struct platform_device *pdev, struct lba_data *data);
+
+
+int lba_core_init(struct lba_data *data);
+int lba_core_remove(struct lba_data *data);
+int lba_core_suspend(struct platform_device *pdev, struct lba_data *data);
+int lba_core_resume(struct platform_device *pdev, struct lba_data *data);
+int lba_core_lock_mode(struct lba_data *data, int mode);
+int lba_core_unlock_mode(struct lba_data *data);
+
+int lba_write_sectors(void *priv, unsigned int sector, unsigned int count,
+ void *buffer, dma_addr_t handle);
+int lba_read_sectors(void *priv, unsigned int sector, unsigned int count,
+ void *buffer, dma_addr_t handle);
+void lba_protocol1_set(void *priv, uint8_t param);
+uint8_t lba_protocol1_get(void *priv);
+uint8_t lba_get_status1(void *priv);
+uint8_t lba_get_status2(void *priv);
+
+uint8_t lba_get_id1(void *priv, uint8_t *ret_buffer);
+uint8_t lba_get_id2(void *priv, uint8_t *);
+
+
+int queue_cmd(void *priv,
+ uint8_t *cmd_buf, dma_addr_t cmd_handle, int cmd_len,
+ dma_addr_t data, int data_len,
+ struct lba_cmd *cmd_flags);
+
+int queue_run(void *priv);
+
+dma_addr_t queue_get_cmd_handle(void *priv);
+
+uint8_t *queue_get_cmd_ptr(void *priv);
+
+dma_addr_t queue_get_data_handle(void *priv);
+
+uint8_t *queue_get_data_ptr(void *priv);
+
+void queue_plug(struct lba_data *data);
+void queue_release(struct lba_data *data);
+
+
+#endif
diff --git a/drivers/mtd/nand/mxc_nd.c b/drivers/mtd/nand/mxc_nd.c
new file mode 100644
index 000000000000..fc95af3bd442
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd.c
@@ -0,0 +1,1413 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/mach/flash.h>
+
+#include "mxc_nd.h"
+
+#define DVR_VER "2.1"
+
+struct mxc_mtd_s {
+ struct mtd_info mtd;
+ struct nand_chip nand;
+ struct mtd_partition *parts;
+ struct device *dev;
+};
+
+static struct mxc_mtd_s *mxc_nand_data;
+
+/*
+ * Define delays in microsec for NAND device operations
+ */
+#define TROP_US_DELAY 2000
+/*
+ * Macros to get half word and bit positions of ECC
+ */
+#define COLPOS(x) ((x) >> 4)
+#define BITPOS(x) ((x) & 0xf)
+
+/* Define single bit Error positions in Main & Spare area */
+#define MAIN_SINGLEBIT_ERROR 0x4
+#define SPARE_SINGLEBIT_ERROR 0x1
+
+struct nand_info {
+ bool bSpareOnly;
+ bool bStatusRequest;
+ u16 colAddr;
+};
+
+static struct nand_info g_nandfc_info;
+
+#ifdef CONFIG_MTD_NAND_MXC_SWECC
+static int hardware_ecc = 0;
+#else
+static int hardware_ecc = 1;
+#endif
+
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+static int Ecc_disabled;
+#endif
+
+static int is2k_Pagesize = 0;
+
+static struct clk *nfc_clk;
+
+/*
+ * OOB placement block for use with hardware ecc generation
+ */
+static struct nand_ecclayout nand_hw_eccoob_8 = {
+ .eccbytes = 5,
+ .eccpos = {6, 7, 8, 9, 10},
+ .oobfree = {{0, 5}, {11, 5}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_16 = {
+ .eccbytes = 5,
+ .eccpos = {6, 7, 8, 9, 10},
+ .oobfree = {{0, 6}, {12, 4}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_2k = {
+ .eccbytes = 20,
+ .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
+ 38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
+ .oobfree = {
+ {.offset = 0,
+ .length = 5},
+
+ {.offset = 11,
+ .length = 10},
+
+ {.offset = 27,
+ .length = 10},
+
+ {.offset = 43,
+ .length = 10},
+
+ {.offset = 59,
+ .length = 5}
+ }
+};
+
+/*!
+ * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors
+ */
+
+/*!
+ * @file mxc_nd.c
+ *
+ * @brief This file contains the hardware specific layer for NAND Flash on
+ * MXC processor
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
+#endif
+
+static wait_queue_head_t irq_waitq;
+
+static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
+{
+ NFC_CONFIG1 |= NFC_INT_MSK; /* Disable interrupt */
+ wake_up(&irq_waitq);
+
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This function polls the NANDFC to wait for the basic operation to complete by
+ * checking the INT bit of config2 register.
+ *
+ * @param maxRetries number of retry attempts (separated by 1 us)
+ * @param param parameter for debug
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void wait_op_done(int maxRetries, u16 param, bool useirq)
+{
+ if (useirq) {
+ if ((NFC_CONFIG2 & NFC_INT) == 0) {
+ NFC_CONFIG1 &= ~NFC_INT_MSK; /* Enable interrupt */
+ wait_event(irq_waitq, NFC_CONFIG2 & NFC_INT);
+ }
+ NFC_CONFIG2 &= ~NFC_INT;
+ } else {
+ while (maxRetries-- > 0) {
+ if (NFC_CONFIG2 & NFC_INT) {
+ NFC_CONFIG2 &= ~NFC_INT;
+ break;
+ }
+ udelay(1);
+ }
+ if (maxRetries <= 0)
+ DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
+ __FUNCTION__, param);
+ }
+}
+
+/*!
+ * This function issues the specified command to the NAND device and
+ * waits for completion.
+ *
+ * @param cmd command for NAND Flash
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void send_cmd(u16 cmd, bool useirq)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq);
+
+ NFC_FLASH_CMD = (u16) cmd;
+ NFC_CONFIG2 = NFC_CMD;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, cmd, useirq);
+}
+
+/*!
+ * This function sends an address (or partial address) to the
+ * NAND device. The address is used to select the source/destination for
+ * a NAND command.
+ *
+ * @param addr address to be written to NFC.
+ * @param islast True if this is the last address cycle for command
+ */
+static void send_addr(u16 addr, bool islast)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, islast);
+
+ NFC_FLASH_ADDR = addr;
+ NFC_CONFIG2 = NFC_ADDR;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, addr, islast);
+}
+
+/*!
+ * This function requests the NANDFC to initate the transfer
+ * of data currently in the NANDFC RAM buffer to the NAND device.
+ *
+ * @param buf_id Specify Internal RAM Buffer number (0-3)
+ * @param bSpareOnly set true if only the spare area is transferred
+ */
+static void send_prog_page(u8 buf_id, bool bSpareOnly)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", bSpareOnly);
+
+ /* NANDFC buffer 0 is used for page read/write */
+
+ NFC_BUF_ADDR = buf_id;
+
+ /* Configure spare or page+spare access */
+ if (!is2k_Pagesize) {
+ if (bSpareOnly) {
+ NFC_CONFIG1 |= NFC_SP_EN;
+ } else {
+ NFC_CONFIG1 &= ~(NFC_SP_EN);
+ }
+ }
+ NFC_CONFIG2 = NFC_INPUT;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, bSpareOnly, true);
+}
+
+/*!
+ * This function will correct the single bit ECC error
+ *
+ * @param buf_id Specify Internal RAM Buffer number (0-3)
+ * @param eccpos Ecc byte and bit position
+ * @param bSpareOnly set to true if only spare area needs correction
+ */
+
+static void mxc_nd_correct_error(u8 buf_id, u16 eccpos, bool bSpareOnly)
+{
+ u16 col;
+ u8 pos;
+ volatile u16 *buf;
+
+ /* Get col & bit position of error
+ these macros works for both 8 & 16 bits */
+ col = COLPOS(eccpos); /* Get half-word position */
+ pos = BITPOS(eccpos); /* Get bit position */
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nd_correct_error (col=%d pos=%d)\n", col, pos);
+
+ /* Set the pointer for main / spare area */
+ if (!bSpareOnly) {
+ buf = (volatile u16 *)(MAIN_AREA0 + col + (256 * buf_id));
+ } else {
+ buf = (volatile u16 *)(SPARE_AREA0 + col + (8 * buf_id));
+ }
+
+ /* Fix the data */
+ *buf ^= (1 << pos);
+}
+
+/*!
+ * This function will maintains state of single bit Error
+ * in Main & spare area
+ *
+ * @param buf_id Specify Internal RAM Buffer number (0-3)
+ * @param spare set to true if only spare area needs correction
+ */
+static void mxc_nd_correct_ecc(u8 buf_id, bool spare)
+{
+#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ static int lastErrMain = 0, lastErrSpare = 0; /* To maintain single bit
+ error in previous page */
+#endif
+ u16 value, ecc_status;
+ /* Read the ECC result */
+ ecc_status = NFC_ECC_STATUS_RESULT;
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nd_correct_ecc (Ecc status=%x)\n", ecc_status);
+
+#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ /* Check for Error in Mainarea */
+ if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) {
+ /* Check for error in previous page */
+ if (lastErrMain && !spare) {
+ value = NFC_RSLTMAIN_AREA;
+ /* Correct single bit error in Mainarea
+ NFC will not correct the error in
+ current page */
+ mxc_nd_correct_error(buf_id, value, false);
+ } else {
+ /* Set if single bit error in current page */
+ lastErrMain = 1;
+ }
+ } else {
+ /* Reset if no single bit error in current page */
+ lastErrMain = 0;
+ }
+
+ /* Check for Error in Sparearea */
+ if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) {
+ /* Check for error in previous page */
+ if (lastErrSpare) {
+ value = NFC_RSLTSPARE_AREA;
+ /* Correct single bit error in Mainarea
+ NFC will not correct the error in
+ current page */
+ mxc_nd_correct_error(buf_id, value, true);
+ } else {
+ /* Set if single bit error in current page */
+ lastErrSpare = 1;
+ }
+ } else {
+ /* Reset if no single bit error in current page */
+ lastErrSpare = 0;
+ }
+#else
+ if (((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR)
+ || ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR)) {
+ if (Ecc_disabled) {
+ if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) {
+ value = NFC_RSLTMAIN_AREA;
+ /* Correct single bit error in Mainarea
+ NFC will not correct the error in
+ current page */
+ mxc_nd_correct_error(buf_id, value, false);
+ }
+ if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) {
+ value = NFC_RSLTSPARE_AREA;
+ /* Correct single bit error in Mainarea
+ NFC will not correct the error in
+ current page */
+ mxc_nd_correct_error(buf_id, value, true);
+ }
+
+ } else {
+ /* Disable ECC */
+ NFC_CONFIG1 &= ~(NFC_ECC_EN);
+ Ecc_disabled = 1;
+ }
+ } else if (ecc_status == 0) {
+ if (Ecc_disabled) {
+ /* Enable ECC */
+ NFC_CONFIG1 |= NFC_ECC_EN;
+ Ecc_disabled = 0;
+ }
+ } else {
+ /* 2-bit Error Do nothing */
+ }
+#endif /* CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 */
+
+}
+
+/*!
+ * This function requests the NANDFC to initated the transfer
+ * of data from the NAND device into in the NANDFC ram buffer.
+ *
+ * @param buf_id Specify Internal RAM Buffer number (0-3)
+ * @param bSpareOnly set true if only the spare area is transferred
+ */
+static void send_read_page(u8 buf_id, bool bSpareOnly)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", bSpareOnly);
+
+ /* NANDFC buffer 0 is used for page read/write */
+ NFC_BUF_ADDR = buf_id;
+
+ /* Configure spare or page+spare access */
+ if (!is2k_Pagesize) {
+ if (bSpareOnly) {
+ NFC_CONFIG1 |= NFC_SP_EN;
+ } else {
+ NFC_CONFIG1 &= ~(NFC_SP_EN);
+ }
+ }
+
+ NFC_CONFIG2 = NFC_OUTPUT;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, bSpareOnly, true);
+
+ /* If there are single bit errors in
+ two consecutive page reads then
+ the error is not corrected by the
+ NFC for the second page.
+ Correct single bit error in driver */
+
+ mxc_nd_correct_ecc(buf_id, bSpareOnly);
+}
+
+/*!
+ * This function requests the NANDFC to perform a read of the
+ * NAND device ID.
+ */
+static void send_read_id(void)
+{
+ struct nand_chip *this = &mxc_nand_data->nand;
+
+ /* NANDFC buffer 0 is used for device ID output */
+ NFC_BUF_ADDR = 0x0;
+
+ /* Read ID into main buffer */
+ NFC_CONFIG1 &= (~(NFC_SP_EN));
+ NFC_CONFIG2 = NFC_ID;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, 0, true);
+
+ if (this->options & NAND_BUSWIDTH_16) {
+ volatile u16 *mainBuf = MAIN_AREA0;
+
+ /*
+ * Pack the every-other-byte result for 16-bit ID reads
+ * into every-byte as the generic code expects and various
+ * chips implement.
+ */
+
+ mainBuf[0] = (mainBuf[0] & 0xff) | ((mainBuf[1] & 0xff) << 8);
+ mainBuf[1] = (mainBuf[2] & 0xff) | ((mainBuf[3] & 0xff) << 8);
+ mainBuf[2] = (mainBuf[4] & 0xff) | ((mainBuf[5] & 0xff) << 8);
+ }
+}
+
+/*!
+ * This function requests the NANDFC to perform a read of the
+ * NAND device status and returns the current status.
+ *
+ * @return device status
+ */
+static u16 get_dev_status(void)
+{
+ volatile u16 *mainBuf = MAIN_AREA1;
+ u32 store;
+ u16 ret;
+ /* Issue status request to NAND device */
+
+ /* store the main area1 first word, later do recovery */
+ store = *((u32 *) mainBuf);
+ /*
+ * NANDFC buffer 1 is used for device status to prevent
+ * corruption of read/write buffer on status requests.
+ */
+ NFC_BUF_ADDR = 1;
+
+ /* Read status into main buffer */
+ NFC_CONFIG1 &= (~(NFC_SP_EN));
+ NFC_CONFIG2 = NFC_STATUS;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, 0, true);
+
+ /* Status is placed in first word of main buffer */
+ /* get status, then recovery area 1 data */
+ ret = mainBuf[0];
+ *((u32 *) mainBuf) = store;
+
+ return ret;
+}
+
+/*!
+ * This functions is used by upper layer to checks if device is ready
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return 0 if device is busy else 1
+ */
+static int mxc_nand_dev_ready(struct mtd_info *mtd)
+{
+ /*
+ * NFC handles R/B internally.Therefore,this function
+ * always returns status as ready.
+ */
+ return 1;
+}
+
+static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ /*
+ * If HW ECC is enabled, we turn it on during init. There is
+ * no need to enable again here.
+ */
+}
+
+static int mxc_nand_correct_data(struct mtd_info *mtd, u_char * dat,
+ u_char * read_ecc, u_char * calc_ecc)
+{
+ /*
+ * 1-Bit errors are automatically corrected in HW. No need for
+ * additional correction. 2-Bit errors cannot be corrected by
+ * HW ECC, so we need to return failure
+ */
+ u16 ecc_status = NFC_ECC_STATUS_RESULT;
+
+ if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
+ u_char * ecc_code)
+{
+ /*
+ * Just return success. HW ECC does not read/write the NFC spare
+ * buffer. Only the FLASH spare area contains the calcuated ECC.
+ */
+ return 0;
+}
+
+/*!
+ * This function reads byte from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static u_char mxc_nand_read_byte(struct mtd_info *mtd)
+{
+ u_char retVal = 0;
+ u16 col, rdWord;
+ volatile u16 *mainBuf = MAIN_AREA0;
+ volatile u16 *spareBuf = SPARE_AREA0;
+
+ /* Check for status request */
+ if (g_nandfc_info.bStatusRequest) {
+ return (get_dev_status() & 0xFF);
+ }
+
+ /* Get column for 16-bit access */
+ col = g_nandfc_info.colAddr >> 1;
+
+ /* If we are accessing the spare region */
+ if (g_nandfc_info.bSpareOnly) {
+ rdWord = spareBuf[col];
+ } else {
+ rdWord = mainBuf[col];
+ }
+
+ /* Pick upper/lower byte of word from RAM buffer */
+ if (g_nandfc_info.colAddr & 0x1) {
+ retVal = (rdWord >> 8) & 0xFF;
+ } else {
+ retVal = rdWord & 0xFF;
+ }
+
+ /* Update saved column address */
+ g_nandfc_info.colAddr++;
+
+ return retVal;
+}
+
+/*!
+ * This function reads word from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static u16 mxc_nand_read_word(struct mtd_info *mtd)
+{
+ u16 col;
+ u16 rdWord, retVal;
+ volatile u16 *p;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_read_word(col = %d)\n", g_nandfc_info.colAddr);
+
+ col = g_nandfc_info.colAddr;
+ /* Adjust saved column address */
+ if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+ col += mtd->writesize;
+
+ if (col < mtd->writesize)
+ p = (MAIN_AREA0) + (col >> 1);
+ else
+ p = (SPARE_AREA0) + ((col - mtd->writesize) >> 1);
+
+ if (col & 1) {
+ rdWord = *p;
+ retVal = (rdWord >> 8) & 0xff;
+ rdWord = *(p + 1);
+ retVal |= (rdWord << 8) & 0xff00;
+
+ } else {
+ retVal = *p;
+
+ }
+
+ /* Update saved column address */
+ g_nandfc_info.colAddr = col + 2;
+
+ return retVal;
+}
+
+/*!
+ * This function writes data of length \b len to buffer \b buf. The data to be
+ * written on NAND Flash is first copied to RAMbuffer. After the Data Input
+ * Operation by the NFC, the data is written to NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be written to NAND Flash
+ * @param len number of bytes to be written
+ */
+static void mxc_nand_write_buf(struct mtd_info *mtd,
+ const u_char * buf, int len)
+{
+ int n;
+ int col;
+ int i = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_write_buf(col = %d, len = %d)\n", g_nandfc_info.colAddr,
+ len);
+
+ col = g_nandfc_info.colAddr;
+
+ /* Adjust saved column address */
+ if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+ col += mtd->writesize;
+
+ n = mtd->writesize + mtd->oobsize - col;
+ n = min(len, n);
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "%s:%d: col = %d, n = %d\n", __FUNCTION__, __LINE__, col, n);
+
+ while (n) {
+ volatile u32 *p;
+ if (col < mtd->writesize)
+ p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3));
+ else
+ p = (volatile u32 *)((ulong) (SPARE_AREA0) -
+ mtd->writesize + (col & ~3));
+
+ DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __FUNCTION__,
+ __LINE__, p);
+
+ if (((col | (int)&buf[i]) & 3) || n < 16) {
+ u32 data = 0;
+
+ if (col & 3 || n < 4)
+ data = *p;
+
+ switch (col & 3) {
+ case 0:
+ if (n) {
+ data = (data & 0xffffff00) |
+ (buf[i++] << 0);
+ n--;
+ col++;
+ }
+ case 1:
+ if (n) {
+ data = (data & 0xffff00ff) |
+ (buf[i++] << 8);
+ n--;
+ col++;
+ }
+ case 2:
+ if (n) {
+ data = (data & 0xff00ffff) |
+ (buf[i++] << 16);
+ n--;
+ col++;
+ }
+ case 3:
+ if (n) {
+ data = (data & 0x00ffffff) |
+ (buf[i++] << 24);
+ n--;
+ col++;
+ }
+ }
+
+ *p = data;
+ } else {
+ int m = mtd->writesize - col;
+
+ if (col >= mtd->writesize)
+ m += mtd->oobsize;
+
+ m = min(n, m) & ~3;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "%s:%d: n = %d, m = %d, i = %d, col = %d\n",
+ __FUNCTION__, __LINE__, n, m, i, col);
+
+ memcpy((void *)(p), &buf[i], m);
+ col += m;
+ i += m;
+ n -= m;
+ }
+ }
+ /* Update saved column address */
+ g_nandfc_info.colAddr = col;
+
+}
+
+/*!
+ * This function id is used to read the data buffer from the NAND Flash. To
+ * read the data from NAND Flash first the data output cycle is initiated by
+ * the NFC, which copies the data to RAMbuffer. This data of length \b len is
+ * then copied to buffer \b buf.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be read from NAND Flash
+ * @param len number of bytes to be read
+ */
+static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
+{
+
+ int n;
+ int col;
+ int i = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_read_buf(col = %d, len = %d)\n", g_nandfc_info.colAddr,
+ len);
+
+ col = g_nandfc_info.colAddr;
+ /* Adjust saved column address */
+ if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+ col += mtd->writesize;
+
+ n = mtd->writesize + mtd->oobsize - col;
+ n = min(len, n);
+
+ while (n) {
+ volatile u32 *p;
+
+ if (col < mtd->writesize)
+ p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3));
+ else
+ p = (volatile u32 *)((ulong) (SPARE_AREA0) -
+ mtd->writesize + (col & ~3));
+
+ if (((col | (int)&buf[i]) & 3) || n < 16) {
+ u32 data;
+
+ data = *p;
+ switch (col & 3) {
+ case 0:
+ if (n) {
+ buf[i++] = (u8) (data);
+ n--;
+ col++;
+ }
+ case 1:
+ if (n) {
+ buf[i++] = (u8) (data >> 8);
+ n--;
+ col++;
+ }
+ case 2:
+ if (n) {
+ buf[i++] = (u8) (data >> 16);
+ n--;
+ col++;
+ }
+ case 3:
+ if (n) {
+ buf[i++] = (u8) (data >> 24);
+ n--;
+ col++;
+ }
+ }
+ } else {
+ int m = mtd->writesize - col;
+
+ if (col >= mtd->writesize)
+ m += mtd->oobsize;
+
+ m = min(n, m) & ~3;
+ memcpy(&buf[i], (void *)(p), m);
+ col += m;
+ i += m;
+ n -= m;
+ }
+ }
+ /* Update saved column address */
+ g_nandfc_info.colAddr = col;
+
+}
+
+/*!
+ * This function is used by the upper layer to verify the data in NAND Flash
+ * with the data in the \b buf.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be verified
+ * @param len length of the data to be verified
+ *
+ * @return -EFAULT if error else 0
+ *
+ */
+static int
+mxc_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
+{
+ return -EFAULT;
+}
+
+/*!
+ * This function is used by upper layer for select and deselect of the NAND
+ * chip
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param chip val indicating select or deselect
+ */
+static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE
+ if (chip > 0) {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "ERROR: Illegal chip select (chip = %d)\n", chip);
+ return;
+ }
+
+ if (chip == -1) {
+ NFC_CONFIG1 &= (~(NFC_CE));
+ return;
+ }
+
+ NFC_CONFIG1 |= NFC_CE;
+#endif
+
+ switch (chip) {
+ case -1:
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+ break;
+ case 0:
+ /* Enable the NFC clock */
+ clk_enable(nfc_clk);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*!
+ * This function is used by the upper layer to write command to NAND Flash for
+ * different operations to be carried out on NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param command command for NAND Flash
+ * @param column column offset for the page read
+ * @param page_addr page to be read from NAND Flash
+ */
+static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ bool useirq = true;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
+ command, column, page_addr);
+
+ /*
+ * Reset command state information
+ */
+ g_nandfc_info.bStatusRequest = false;
+
+ /* Reset column address to 0 */
+ g_nandfc_info.colAddr = 0;
+
+ /*
+ * Command pre-processing step
+ */
+ switch (command) {
+
+ case NAND_CMD_STATUS:
+ g_nandfc_info.bStatusRequest = true;
+ break;
+
+ case NAND_CMD_READ0:
+ g_nandfc_info.colAddr = column;
+ g_nandfc_info.bSpareOnly = false;
+ useirq = false;
+ break;
+
+ case NAND_CMD_READOOB:
+ g_nandfc_info.colAddr = column;
+ g_nandfc_info.bSpareOnly = true;
+ useirq = false;
+ if (is2k_Pagesize)
+ command = NAND_CMD_READ0; /* only READ0 is valid */
+ break;
+
+ case NAND_CMD_SEQIN:
+ if (column >= mtd->writesize) {
+ if (is2k_Pagesize) {
+ /**
+ * FIXME: before send SEQIN command for write OOB,
+ * We must read one page out.
+ * For K9F1GXX has no READ1 command to set current HW
+ * pointer to spare area, we must write the whole page including OOB together.
+ */
+ /* call itself to read a page */
+ mxc_nand_command(mtd, NAND_CMD_READ0, 0,
+ page_addr);
+ }
+ g_nandfc_info.colAddr = column - mtd->writesize;
+ g_nandfc_info.bSpareOnly = true;
+ /* Set program pointer to spare region */
+ if (!is2k_Pagesize)
+ send_cmd(NAND_CMD_READOOB, false);
+ } else {
+ g_nandfc_info.bSpareOnly = false;
+ g_nandfc_info.colAddr = column;
+ /* Set program pointer to page start */
+ if (!is2k_Pagesize)
+ send_cmd(NAND_CMD_READ0, false);
+ }
+ useirq = false;
+ break;
+
+ case NAND_CMD_PAGEPROG:
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ if (Ecc_disabled) {
+ /* Enable Ecc for page writes */
+ NFC_CONFIG1 |= NFC_ECC_EN;
+ }
+#endif
+
+ send_prog_page(0, g_nandfc_info.bSpareOnly);
+
+ if (is2k_Pagesize) {
+ /* data in 4 areas datas */
+ send_prog_page(1, g_nandfc_info.bSpareOnly);
+ send_prog_page(2, g_nandfc_info.bSpareOnly);
+ send_prog_page(3, g_nandfc_info.bSpareOnly);
+ }
+
+ break;
+
+ case NAND_CMD_ERASE1:
+ useirq = false;
+ break;
+ }
+
+ /*
+ * Write out the command to the device.
+ */
+ send_cmd(command, useirq);
+
+ /*
+ * Write out column address, if necessary
+ */
+ if (column != -1) {
+ /*
+ * MXC NANDFC can only perform full page+spare or
+ * spare-only read/write. When the upper layers
+ * layers perform a read/write buf operation,
+ * we will used the saved column adress to index into
+ * the full page.
+ */
+ send_addr(0, page_addr == -1);
+ if (is2k_Pagesize)
+ /* another col addr cycle for 2k page */
+ send_addr(0, false);
+ }
+
+ /*
+ * Write out page address, if necessary
+ */
+ if (page_addr != -1) {
+ send_addr((page_addr & 0xff), false); /* paddr_0 - p_addr_7 */
+
+ if (is2k_Pagesize) {
+ /* One more address cycle for higher density devices */
+ if (mtd->size >= 0x10000000) {
+ /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 8) & 0xff, false);
+ send_addr((page_addr >> 16) & 0xff, true);
+ } else
+ /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 8) & 0xff, true);
+ } else {
+ /* One more address cycle for higher density devices */
+ if (mtd->size >= 0x4000000) {
+ /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 8) & 0xff, false);
+ send_addr((page_addr >> 16) & 0xff, true);
+ } else
+ /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 8) & 0xff, true);
+ }
+ }
+
+ /*
+ * Command post-processing step
+ */
+ switch (command) {
+
+ case NAND_CMD_RESET:
+ break;
+
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READ0:
+ if (is2k_Pagesize) {
+ /* send read confirm command */
+ send_cmd(NAND_CMD_READSTART, true);
+ /* read for each AREA */
+ send_read_page(0, g_nandfc_info.bSpareOnly);
+ send_read_page(1, g_nandfc_info.bSpareOnly);
+ send_read_page(2, g_nandfc_info.bSpareOnly);
+ send_read_page(3, g_nandfc_info.bSpareOnly);
+ } else {
+ send_read_page(0, g_nandfc_info.bSpareOnly);
+ }
+ break;
+
+ case NAND_CMD_READID:
+ send_read_id();
+ break;
+
+ case NAND_CMD_PAGEPROG:
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ if (Ecc_disabled) {
+ /* Disble Ecc after page writes */
+ NFC_CONFIG1 &= ~(NFC_ECC_EN);
+ }
+#endif
+ break;
+
+ case NAND_CMD_STATUS:
+ break;
+
+ case NAND_CMD_ERASE2:
+ break;
+ }
+}
+
+/* Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks. */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 5,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_memorybased = {
+ .options = 0,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+static int mxc_nand_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ /* Config before scanning */
+ /* Do not rely on NFMS_BIT, set/clear NFMS bit based on mtd->writesize */
+ if (mtd->writesize == 2048) {
+ NFMS |= (1 << NFMS_BIT);
+ is2k_Pagesize = 1;
+ } else {
+ if ((NFMS >> NFMS_BIT) & 0x1) { /* This case strangly happened on MXC91321 P1.2.2 */
+ printk(KERN_INFO
+ "Oops... NFMS Bit set for 512B Page, resetting it. [RCSR: 0x%08x]\n",
+ NFMS);
+ NFMS &= ~(1 << NFMS_BIT);
+ }
+ is2k_Pagesize = 0;
+ }
+
+ if (is2k_Pagesize)
+ this->ecc.layout = &nand_hw_eccoob_2k;
+
+ /* jffs2 not write oob */
+ mtd->flags &= ~MTD_OOB_WRITEABLE;
+
+ /* use flash based bbt */
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+
+ /* update flash based bbt */
+ this->options |= NAND_USE_FLASH_BBT;
+
+ if (!this->badblock_pattern) {
+ if (mtd->writesize == 2048)
+ this->badblock_pattern = &smallpage_memorybased;
+ else
+ this->badblock_pattern = (mtd->writesize > 512) ?
+ &largepage_memorybased : &smallpage_memorybased;
+ }
+ /* Build bad block table */
+ return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE
+static void mxc_low_erase(struct mtd_info *mtd)
+{
+
+ struct nand_chip *this = mtd->priv;
+ unsigned int page_addr, addr;
+ u_char status;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : mxc_low_erase:Erasing NAND\n");
+ for (addr = 0; addr < this->chipsize; addr += mtd->erasesize) {
+ page_addr = addr / mtd->writesize;
+ mxc_nand_command(mtd, NAND_CMD_ERASE1, -1, page_addr);
+ mxc_nand_command(mtd, NAND_CMD_ERASE2, -1, -1);
+ mxc_nand_command(mtd, NAND_CMD_STATUS, -1, -1);
+ status = mxc_nand_read_byte(mtd);
+ if (status & NAND_STATUS_FAIL) {
+ printk(KERN_ERR
+ "ERASE FAILED(block = %d,status = 0x%x)\n",
+ addr / mtd->erasesize, status);
+ }
+ }
+
+}
+#endif
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and
+ * remove functions
+ *
+ * @return The function always returns 0.
+ */
+static int __init mxcnd_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+ int nr_parts = 0;
+
+ int err = 0;
+ /* Allocate memory for MTD device structure and private data */
+ mxc_nand_data = kzalloc(sizeof(struct mxc_mtd_s), GFP_KERNEL);
+ if (!mxc_nand_data) {
+ printk(KERN_ERR "%s: failed to allocate mtd_info\n",
+ __FUNCTION__);
+ err = -ENOMEM;
+ goto out;
+ }
+ memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info));
+
+ mxc_nand_data->dev = &pdev->dev;
+ /* structures must be linked */
+ this = &mxc_nand_data->nand;
+ mtd = &mxc_nand_data->mtd;
+ mtd->priv = this;
+ mtd->owner = THIS_MODULE;
+
+ /* 50 us command delay time */
+ this->chip_delay = 5;
+
+ this->priv = mxc_nand_data;
+ this->dev_ready = mxc_nand_dev_ready;
+ this->cmdfunc = mxc_nand_command;
+ this->select_chip = mxc_nand_select_chip;
+ this->read_byte = mxc_nand_read_byte;
+ this->read_word = mxc_nand_read_word;
+ this->write_buf = mxc_nand_write_buf;
+ this->read_buf = mxc_nand_read_buf;
+ this->verify_buf = mxc_nand_verify_buf;
+ this->scan_bbt = mxc_nand_scan_bbt;
+
+ nfc_clk = clk_get(&pdev->dev, "nfc_clk");
+ clk_enable(nfc_clk);
+
+ NFC_CONFIG1 |= NFC_INT_MSK;
+ init_waitqueue_head(&irq_waitq);
+ err = request_irq(MXC_INT_NANDFC, mxc_nfc_irq, 0, "mxc_nd", NULL);
+ if (err) {
+ goto out_1;
+ }
+
+ if (hardware_ecc) {
+ this->ecc.calculate = mxc_nand_calculate_ecc;
+ this->ecc.hwctl = mxc_nand_enable_hwecc;
+ this->ecc.correct = mxc_nand_correct_data;
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.size = 512;
+ this->ecc.bytes = 3;
+ this->ecc.layout = &nand_hw_eccoob_8;
+ NFC_CONFIG1 |= NFC_ECC_EN;
+ } else {
+ this->ecc.mode = NAND_ECC_SOFT;
+ }
+
+ /* Reset NAND */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* preset operation */
+ /* Unlock the internal RAM Buffer */
+ NFC_CONFIG = 0x2;
+
+ /* Blocks to be unlocked */
+ NFC_UNLOCKSTART_BLKADDR = 0x0;
+ NFC_UNLOCKEND_BLKADDR = 0x4000;
+
+ /* Unlock Block Command for given address range */
+ NFC_WRPROT = 0x4;
+
+ /* NAND bus width determines access funtions used by upper layer */
+ if (flash->width == 2) {
+ this->options |= NAND_BUSWIDTH_16;
+ this->ecc.layout = &nand_hw_eccoob_16;
+ } else {
+ this->options |= 0;
+ }
+
+ is2k_Pagesize = 0;
+
+ /* Scan to find existence of the device */
+ if (nand_scan(mtd, 1)) {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "MXC_ND: Unable to find any NAND device.\n");
+ err = -ENXIO;
+ goto out_1;
+ }
+
+ /* Register the partitions */
+#ifdef CONFIG_MTD_PARTITIONS
+ nr_parts =
+ parse_mtd_partitions(mtd, part_probes, &mxc_nand_data->parts, 0);
+ if (nr_parts > 0)
+ add_mtd_partitions(mtd, mxc_nand_data->parts, nr_parts);
+ else if (flash->parts)
+ add_mtd_partitions(mtd, flash->parts, flash->nr_parts);
+ else
+#endif
+ {
+ pr_info("Registering %s as whole device\n", mtd->name);
+ add_mtd_device(mtd);
+ }
+#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE
+ /* Erase all the blocks of a NAND */
+ mxc_low_erase(mtd);
+#endif
+
+ platform_set_drvdata(pdev, mtd);
+ return 0;
+
+ out_1:
+ kfree(mxc_nand_data);
+ out:
+ return err;
+
+}
+
+ /*!
+ * Dissociates the driver from the device.
+ *
+ * @param pdev the device structure used to give information on which
+ *
+ * @return The function always returns 0.
+ */
+
+static int __exit mxcnd_remove(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+
+ clk_put(nfc_clk);
+ platform_set_drvdata(pdev, NULL);
+
+ if (mxc_nand_data) {
+ nand_release(mtd);
+ free_irq(MXC_INT_NANDFC, NULL);
+ kfree(mxc_nand_data);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function is called to put the NAND in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device information structure
+ *
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+
+static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n");
+ if (info)
+ ret = info->suspend(info);
+
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+
+ return ret;
+}
+
+/*!
+ * This function is called to bring the NAND back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device information structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxcnd_resume(struct platform_device *pdev)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n");
+ /* Enable the NFC clock */
+ clk_enable(nfc_clk);
+
+ if (info) {
+ info->resume(info);
+ }
+
+ return ret;
+}
+
+#else
+#define mxcnd_suspend NULL
+#define mxcnd_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcnd_driver = {
+ .driver = {
+ .name = "mxc_nand_flash",
+ },
+ .probe = mxcnd_probe,
+ .remove = __exit_p(mxcnd_remove),
+ .suspend = mxcnd_suspend,
+ .resume = mxcnd_resume,
+};
+
+/*!
+ * Main initialization routine
+ * @return 0 if successful; non-zero otherwise
+ */
+static int __init mxc_nd_init(void)
+{
+ /* Register the device driver structure. */
+ pr_info("MXC MTD nand Driver %s\n", DVR_VER);
+ if (platform_driver_register(&mxcnd_driver) != 0) {
+ printk(KERN_ERR "Driver register failed for mxcnd_driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/*!
+ * Clean up routine
+ */
+static void __exit mxc_nd_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mxcnd_driver);
+}
+
+module_init(mxc_nd_init);
+module_exit(mxc_nd_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC NAND MTD driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/mxc_nd.h b/drivers/mtd/nand/mxc_nd.h
new file mode 100644
index 000000000000..e38a9a637c1d
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxc_nd.h
+ *
+ * @brief This file contains the NAND Flash Controller register information.
+ *
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifndef __MXC_ND_H__
+#define __MXC_ND_H__
+
+#include <mach/hardware.h>
+
+/*
+ * Addresses for NFC registers
+ */
+#define NFC_BUF_SIZE (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE00)))
+#define NFC_BUF_ADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE04)))
+#define NFC_FLASH_ADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE06)))
+#define NFC_FLASH_CMD (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE08)))
+#define NFC_CONFIG (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0A)))
+#define NFC_ECC_STATUS_RESULT (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0C)))
+#define NFC_RSLTMAIN_AREA (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0E)))
+#define NFC_RSLTSPARE_AREA (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE10)))
+#define NFC_WRPROT (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE12)))
+#define NFC_UNLOCKSTART_BLKADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE14)))
+#define NFC_UNLOCKEND_BLKADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE16)))
+#define NFC_NF_WRPRST (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE18)))
+#define NFC_CONFIG1 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE1A)))
+#define NFC_CONFIG2 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE1C)))
+
+/*!
+ * Addresses for NFC RAM BUFFER Main area 0
+ */
+#define MAIN_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x000)
+#define MAIN_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x200)
+#define MAIN_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x400)
+#define MAIN_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x600)
+
+/*!
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#define SPARE_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x800)
+#define SPARE_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x810)
+#define SPARE_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x820)
+#define SPARE_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x830)
+
+/*!
+ * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register for Command
+ * operation
+ */
+#define NFC_CMD 0x1
+
+/*!
+ * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register for Address
+ * operation
+ */
+#define NFC_ADDR 0x2
+
+/*!
+ * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register for Input
+ * operation
+ */
+#define NFC_INPUT 0x4
+
+/*!
+ * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register for Data Output
+ * operation
+ */
+#define NFC_OUTPUT 0x8
+
+/*!
+ * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register for Read ID
+ * operation
+ */
+#define NFC_ID 0x10
+
+/*!
+ * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register for Read Status
+ * operation
+ */
+#define NFC_STATUS 0x20
+
+/*!
+ * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read Status
+ * operation
+ */
+#define NFC_INT 0x8000
+
+#define NFC_SP_EN (1 << 2)
+#define NFC_ECC_EN (1 << 3)
+#define NFC_INT_MSK (1 << 4)
+#define NFC_BIG (1 << 5)
+#define NFC_RST (1 << 6)
+#define NFC_CE (1 << 7)
+#define NFC_ONE_CYCLE (1 << 8)
+
+#endif /* MXCND_H */
diff --git a/drivers/mtd/nand/mxc_nd2.c b/drivers/mtd/nand/mxc_nd2.c
new file mode 100644
index 000000000000..65b0fd6cfaff
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd2.c
@@ -0,0 +1,1448 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mtd/partitions.h>
+#include <asm/mach/flash.h>
+#include <asm/io.h>
+#include "mxc_nd2.h"
+
+#define DVR_VER "2.5"
+
+/* Global address Variables */
+static void __iomem *nfc_axi_base, *nfc_ip_base;
+
+struct mxc_mtd_s {
+ struct mtd_info mtd;
+ struct nand_chip nand;
+ struct mtd_partition *parts;
+ struct device *dev;
+};
+
+static struct mxc_mtd_s *mxc_nand_data;
+
+/*
+ * Define delays in microsec for NAND device operations
+ */
+#define TROP_US_DELAY 2000
+
+struct nand_info {
+ bool bStatusRequest;
+ u16 colAddr;
+};
+
+static struct nand_info g_nandfc_info;
+
+#ifdef CONFIG_MTD_NAND_MXC_SWECC
+static int hardware_ecc = 0;
+#else
+static int hardware_ecc = 1;
+#endif
+
+static u8 num_of_interleave = 1;
+
+static u8 *data_buf;
+static u8 *oob_buf;
+
+static int g_page_mask;
+
+static struct clk *nfc_clk;
+
+/*
+ * OOB placement block for use with hardware ecc generation
+ */
+static struct nand_ecclayout nand_hw_eccoob_512 = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 4,
+ .oobfree = {{0, 4}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_2k = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 4,
+ .oobfree = {{2, 4}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_4k = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 4,
+ .oobfree = {{2, 4}}
+};
+
+/*!
+ * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors
+ */
+
+/*!
+ * @file mxc_nd2.c
+ *
+ * @brief This file contains the hardware specific layer for NAND Flash on
+ * MXC processor
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
+#endif
+
+static wait_queue_head_t irq_waitq;
+
+static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
+{
+ /* Disable Interuupt */
+ raw_write(raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK, REG_NFC_INTRRUPT);
+ wake_up(&irq_waitq);
+
+ return IRQ_HANDLED;
+}
+
+static void nfc_memcpy(void *dest, void *src, int len)
+{
+ u8 *d = dest;
+ u8 *s = src;
+
+ while (len > 0) {
+ if (len >= 4) {
+ *(u32 *)d = *(u32 *)s;
+ d += 4;
+ s += 4;
+ len -= 4;
+ } else {
+ *(u16 *)d = *(u16 *)s;
+ len -= 2;
+ break;
+ }
+ }
+
+ if (len)
+ BUG();
+}
+
+/*
+ * Functions to transfer data to/from spare erea.
+ */
+static void
+copy_spare(struct mtd_info *mtd, void *pbuf, void *pspare, int len, bool bfrom)
+{
+ u16 i, j;
+ u16 m = mtd->oobsize;
+ u16 n = mtd->writesize >> 9;
+ u8 *d = (u8 *) pbuf;
+ u8 *s = (u8 *) pspare;
+ u16 t = SPARE_LEN;
+
+ m /= num_of_interleave;
+ n /= num_of_interleave;
+
+ j = (m / n >> 1) << 1;
+
+ if (bfrom) {
+ for (i = 0; i < n - 1; i++)
+ nfc_memcpy(&d[i * j], &s[i * t], j);
+
+ /* the last section */
+ nfc_memcpy(&d[i * j], &s[i * t], len - i * j);
+ } else {
+ for (i = 0; i < n - 1; i++)
+ nfc_memcpy(&s[i * t], &d[i * j], j);
+
+ /* the last section */
+ nfc_memcpy(&s[i * t], &d[i * j], len - i * j);
+ }
+}
+
+/*!
+ * This function polls the NFC to wait for the basic operation to complete by
+ * checking the INT bit of config2 register.
+ *
+ * @param maxRetries number of retry attempts (separated by 1 us)
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void wait_op_done(int maxRetries, bool useirq)
+{
+ if (useirq) {
+ if ((raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT) == 0) {
+ /* Enable Interuupt */
+ raw_write(raw_read(REG_NFC_INTRRUPT) & ~NFC_INT_MSK,
+ REG_NFC_INTRRUPT);
+ wait_event(irq_waitq,
+ (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT));
+ }
+ WRITE_NFC_IP_REG((raw_read(REG_NFC_OPS_STAT) &
+ ~NFC_OPS_STAT), REG_NFC_OPS_STAT);
+ } else {
+ while (1) {
+ maxRetries--;
+ if (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT) {
+ WRITE_NFC_IP_REG((raw_read(REG_NFC_OPS_STAT) &
+ ~NFC_OPS_STAT),
+ REG_NFC_OPS_STAT);
+ break;
+ }
+ udelay(1);
+ if (maxRetries <= 0) {
+ DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
+ __func__, __LINE__);
+ }
+ }
+ }
+}
+
+static inline void send_atomic_cmd(u16 cmd, bool useirq)
+{
+ /* fill command */
+ raw_write(cmd, REG_NFC_FLASH_CMD);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* send out command */
+ raw_write(NFC_CMD, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, useirq);
+}
+
+static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr);
+static int mxc_check_ecc_status(struct mtd_info *mtd);
+
+#ifdef NFC_AUTO_MODE_ENABLE
+/*!
+ * This function handle the interleave related work
+ * @param mtd mtd info
+ * @param cmd command
+ */
+static void auto_cmd_interleave(struct mtd_info *mtd, u16 cmd)
+{
+ u32 i, page_addr, ncs;
+ u32 j = num_of_interleave;
+ struct nand_chip *this = mtd->priv;
+ u32 addr_low = raw_read(NFC_FLASH_ADDR0);
+ u32 addr_high = raw_read(NFC_FLASH_ADDR8);
+ u8 *dbuf = data_buf;
+ u8 *obuf = oob_buf;
+ u32 dlen = mtd->writesize / j;
+ u32 olen = mtd->oobsize / j;
+
+ /* adjust the addr value
+ * since ADD_OP mode is 01
+ */
+ if (cmd == NAND_CMD_ERASE2)
+ page_addr = addr_low;
+ else
+ page_addr = addr_low >> 16 | addr_high << 16;
+
+ ncs = page_addr >> (this->chip_shift - this->page_shift);
+
+ if (j > 1) {
+ page_addr *= j;
+ } else {
+ page_addr *= this->numchips;
+ page_addr += ncs;
+ }
+
+ switch (cmd) {
+ case NAND_CMD_PAGEPROG:
+ for (i = 0; i < j; i++) {
+ /* reset addr cycle */
+ mxc_do_addr_cycle(mtd, 0, page_addr++);
+
+ /* data transfer */
+ memcpy(MAIN_AREA0, dbuf, dlen);
+ copy_spare(mtd, obuf, SPARE_AREA0, olen, false);
+
+ /* update the value */
+ dbuf += dlen;
+ obuf += olen;
+
+ NFC_SET_RBA(0);
+ ACK_OPS;
+ raw_write(NFC_AUTO_PROG, REG_NFC_OPS);
+
+ /* wait auto_prog_done bit set */
+ while (!(raw_read(REG_NFC_OPS_STAT) & NFC_OP_DONE)) ;
+ }
+
+ wait_op_done(TROP_US_DELAY, false);
+ while (!(raw_read(REG_NFC_OPS_STAT) & NFC_RB)) ;
+
+ break;
+ case NAND_CMD_READSTART:
+ for (i = 0; i < j; i++) {
+ /* reset addr cycle */
+ mxc_do_addr_cycle(mtd, 0, page_addr++);
+
+ NFC_SET_RBA(0);
+ ACK_OPS;
+ raw_write(NFC_AUTO_READ, REG_NFC_OPS);
+ wait_op_done(TROP_US_DELAY, false);
+
+ /* check ecc error */
+ mxc_check_ecc_status(mtd);
+
+ /* data transfer */
+ memcpy(dbuf, MAIN_AREA0, dlen);
+ copy_spare(mtd, obuf, SPARE_AREA0, olen, true);
+
+ /* update the value */
+ dbuf += dlen;
+ obuf += olen;
+ }
+ break;
+ case NAND_CMD_ERASE2:
+ for (i = 0; i < j; i++) {
+ mxc_do_addr_cycle(mtd, -1, page_addr++);
+ ACK_OPS;
+ raw_write(NFC_AUTO_ERASE, REG_NFC_OPS);
+ wait_op_done(TROP_US_DELAY, true);
+ }
+ break;
+ case NAND_CMD_RESET:
+ for (i = 0; i < j; i++) {
+ if (j > 1)
+ NFC_SET_NFC_ACTIVE_CS(i);
+ send_atomic_cmd(cmd, false);
+ }
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+static void send_addr(u16 addr, bool useirq);
+
+/*!
+ * This function issues the specified command to the NAND device and
+ * waits for completion.
+ *
+ * @param cmd command for NAND Flash
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void send_cmd(struct mtd_info *mtd, u16 cmd, bool useirq)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq);
+
+#ifdef NFC_AUTO_MODE_ENABLE
+ switch (cmd) {
+ case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
+ raw_write(NAND_CMD_READ0, REG_NFC_FLASH_CMD);
+ break;
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_ERASE1:
+ raw_write(cmd, REG_NFC_FLASH_CMD);
+ break;
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_READSTART:
+ raw_write(raw_read(REG_NFC_FLASH_CMD) | cmd << NFC_CMD_1_SHIFT,
+ REG_NFC_FLASH_CMD);
+ auto_cmd_interleave(mtd, cmd);
+ break;
+ case NAND_CMD_READID:
+ send_atomic_cmd(cmd, useirq);
+ send_addr(0, false);
+ break;
+ case NAND_CMD_RESET:
+ auto_cmd_interleave(mtd, cmd);
+ case NAND_CMD_STATUS:
+ break;
+ default:
+ break;
+ }
+#else
+ send_atomic_cmd(cmd, useirq);
+#endif
+}
+
+/*!
+ * This function sends an address (or partial address) to the
+ * NAND device. The address is used to select the source/destination for
+ * a NAND command.
+ *
+ * @param addr address to be written to NFC.
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void send_addr(u16 addr, bool useirq)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, useirq);
+
+ /* fill address */
+ raw_write((addr << NFC_FLASH_ADDR_SHIFT), REG_NFC_FLASH_ADDR);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* send out address */
+ raw_write(NFC_ADDR, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, useirq);
+}
+
+/*!
+ * This function requests the NFC to initate the transfer
+ * of data currently in the NFC RAM buffer to the NAND device.
+ *
+ * @param buf_id Specify Internal RAM Buffer number
+ */
+static void send_prog_page(u8 buf_id)
+{
+#ifndef NFC_AUTO_MODE_ENABLE
+ DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__);
+
+ /* set ram buffer id */
+ NFC_SET_RBA(buf_id);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* transfer data from NFC ram to nand */
+ raw_write(NFC_INPUT, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, false);
+#endif
+}
+
+/*!
+ * This function requests the NFC to initated the transfer
+ * of data from the NAND device into in the NFC ram buffer.
+ *
+ * @param buf_id Specify Internal RAM Buffer number
+ */
+static void send_read_page(u8 buf_id)
+{
+#ifndef NFC_AUTO_MODE_ENABLE
+ DEBUG(MTD_DEBUG_LEVEL3, "%s(%d)\n", __FUNCTION__, buf_id);
+
+ /* set ram buffer id */
+ NFC_SET_RBA(buf_id);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* transfer data from nand to NFC ram */
+ raw_write(NFC_OUTPUT, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, false);
+#endif
+}
+
+/*!
+ * This function requests the NFC to perform a read of the
+ * NAND device ID.
+ */
+static void send_read_id(void)
+{
+ /* Set RBA bits for BUFFER0 */
+ NFC_SET_RBA(0);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* Read ID into main buffer */
+ raw_write(NFC_ID, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, false);
+
+}
+
+#ifdef NFC_AUTO_MODE_ENABLE
+static inline void read_dev_status(u16 *status)
+{
+ u32 mask = 0xFF << 16;
+
+ /* clear status */
+ ACK_OPS;
+
+ do {
+ /* send auto read status command */
+ raw_write(NFC_AUTO_STATE, REG_NFC_OPS);
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) == 1)
+ wait_op_done(TROP_US_DELAY, false);
+ *status = (raw_read(NFC_CONFIG1) & mask) >> 16;
+ } while ((*status & NAND_STATUS_READY) == 0);
+}
+#endif
+
+/*!
+ * This function requests the NFC to perform a read of the
+ * NAND device status and returns the current status.
+ *
+ * @return device status
+ */
+static u16 get_dev_status(void)
+{
+#ifdef NFC_AUTO_MODE_ENABLE
+ int i;
+ u16 status = 0;
+ for (i = 0; i < num_of_interleave; i++) {
+
+ /* set ative cs */
+ NFC_SET_NFC_ACTIVE_CS(i);
+
+ /* FIXME, NFC Auto erase may have
+ * problem, have to pollingit until
+ * the nand get idle, otherwise
+ * it may get error
+ */
+ read_dev_status(&status);
+ if (status & NAND_STATUS_FAIL)
+ break;
+ }
+
+ return status;
+#else
+ volatile u16 *mainBuf = MAIN_AREA1;
+ u8 val = 1;
+ u16 ret;
+
+ /* Set ram buffer id */
+ NFC_SET_RBA(val);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* Read status into main buffer */
+ raw_write(NFC_STATUS, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, false);
+
+ /* Status is placed in first word of main buffer */
+ /* get status, then recovery area 1 data */
+ ret = *mainBuf;
+
+ return ret;
+#endif
+}
+
+static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN), REG_NFC_ECC_EN);
+ return;
+}
+
+/*
+ * Function to record the ECC corrected/uncorrected errors resulted
+ * after a page read. This NFC detects and corrects upto to 4 symbols
+ * of 9-bits each.
+ */
+static int mxc_check_ecc_status(struct mtd_info *mtd)
+{
+ u32 ecc_stat, err;
+ int no_subpages = 1;
+ int ret = 0;
+ u8 ecc_bit_mask, err_limit;
+
+ ecc_bit_mask = (IS_4BIT_ECC ? 0x7 : 0xf);
+ err_limit = (IS_4BIT_ECC ? 0x4 : 0x8);
+
+ no_subpages = mtd->writesize >> 9;
+
+ no_subpages /= num_of_interleave;
+
+ ecc_stat = GET_NFC_ECC_STATUS();
+ do {
+ err = ecc_stat & ecc_bit_mask;
+ if (err > err_limit) {
+ mtd->ecc_stats.failed++;
+ printk(KERN_WARNING "UnCorrectable RS-ECC Error\n");
+ return -1;
+ } else {
+ ret += err;
+ }
+ ecc_stat >>= 4;
+ } while (--no_subpages);
+
+ mtd->ecc_stats.corrected += ret;
+ pr_debug("%d Symbol Correctable RS-ECC Error\n", ret);
+
+ return ret;
+}
+
+/*
+ * Function to correct the detected errors. This NFC corrects all the errors
+ * detected. So this function just return 0.
+ */
+static int mxc_nand_correct_data(struct mtd_info *mtd, u_char * dat,
+ u_char * read_ecc, u_char * calc_ecc)
+{
+ return 0;
+}
+
+/*
+ * Function to calculate the ECC for the data to be stored in the Nand device.
+ * This NFC has a hardware RS(511,503) ECC engine together with the RS ECC
+ * CONTROL blocks are responsible for detection and correction of up to
+ * 8 symbols of 9 bits each in 528 byte page.
+ * So this function is just return 0.
+ */
+
+static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
+ u_char * ecc_code)
+{
+ return 0;
+}
+
+/*!
+ * This function id is used to read the data buffer from the NAND Flash. To
+ * read the data from NAND Flash first the data output cycle is initiated by
+ * the NFC, which copies the data to RAMbuffer. This data of length \b len is
+ * then copied to buffer \b buf.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be read from NAND Flash
+ * @param len number of bytes to be read
+ */
+static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
+{
+ u16 col = g_nandfc_info.colAddr;
+
+ if (mtd->writesize) {
+
+ int j = mtd->writesize - col;
+ int n = mtd->oobsize + j;
+
+ n = min(n, len);
+
+ if (j > 0) {
+ if (n > j) {
+ memcpy(buf, &data_buf[col], j);
+ memcpy(buf + j, &oob_buf[0], n - j);
+ } else {
+ memcpy(buf, &data_buf[col], n);
+ }
+ } else {
+ col -= mtd->writesize;
+ memcpy(buf, &oob_buf[col], len);
+ }
+
+ /* update */
+ g_nandfc_info.colAddr += n;
+
+ } else {
+ /* At flash identify phase,
+ * mtd->writesize has not been
+ * set correctly, it should
+ * be zero.And len will less 2
+ */
+ memcpy(buf, &data_buf[col], len);
+
+ /* update */
+ g_nandfc_info.colAddr += len;
+ }
+
+}
+
+/*!
+ * This function reads byte from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static uint8_t mxc_nand_read_byte(struct mtd_info *mtd)
+{
+ uint8_t ret;
+
+ /* Check for status request */
+ if (g_nandfc_info.bStatusRequest) {
+ return (get_dev_status() & 0xFF);
+ }
+
+ mxc_nand_read_buf(mtd, &ret, 1);
+
+ return ret;
+}
+
+/*!
+ * This function reads word from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static u16 mxc_nand_read_word(struct mtd_info *mtd)
+{
+ u16 ret;
+
+ mxc_nand_read_buf(mtd, (uint8_t *) &ret, sizeof(u16));
+
+ return ret;
+}
+
+/*!
+ * This function reads byte from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static u_char mxc_nand_read_byte16(struct mtd_info *mtd)
+{
+ /* Check for status request */
+ if (g_nandfc_info.bStatusRequest) {
+ return (get_dev_status() & 0xFF);
+ }
+
+ return mxc_nand_read_word(mtd) & 0xFF;
+}
+
+/*!
+ * This function writes data of length \b len from buffer \b buf to the NAND
+ * internal RAM buffer's MAIN area 0.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be written to NAND Flash
+ * @param len number of bytes to be written
+ */
+static void mxc_nand_write_buf(struct mtd_info *mtd,
+ const u_char * buf, int len)
+{
+ u16 col = g_nandfc_info.colAddr;
+ int j = mtd->writesize - col;
+ int n = mtd->oobsize + j;
+
+ n = min(n, len);
+
+ if (j > 0) {
+ if (n > j) {
+ memcpy(&data_buf[col], buf, j);
+ memcpy(&oob_buf[0], buf + j, n - j);
+ } else {
+ memcpy(&data_buf[col], buf, n);
+ }
+ } else {
+ col -= mtd->writesize;
+ memcpy(&oob_buf[col], buf, len);
+ }
+
+ /* update */
+ g_nandfc_info.colAddr += n;
+}
+
+/*!
+ * This function is used by the upper layer to verify the data in NAND Flash
+ * with the data in the \b buf.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be verified
+ * @param len length of the data to be verified
+ *
+ * @return -EFAULT if error else 0
+ *
+ */
+static int mxc_nand_verify_buf(struct mtd_info *mtd, const u_char * buf,
+ int len)
+{
+ u_char *s = data_buf;
+
+ const u_char *p = buf;
+
+ for (; len > 0; len--) {
+ if (*p++ != *s++)
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is used by upper layer for select and deselect of the NAND
+ * chip
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param chip val indicating select or deselect
+ */
+static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+
+ switch (chip) {
+ case -1:
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+ break;
+ case 0 ... 7:
+ /* Enable the NFC clock */
+ clk_enable(nfc_clk);
+
+ NFC_SET_NFC_ACTIVE_CS(chip);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Function to perform the address cycles.
+ */
+static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
+{
+#ifdef NFC_AUTO_MODE_ENABLE
+
+ if (page_addr != -1 && column != -1) {
+ u32 mask = 0xFFFF;
+ /* the column address */
+ raw_write(column & mask, NFC_FLASH_ADDR0);
+ raw_write((raw_read(NFC_FLASH_ADDR0) |
+ ((page_addr & mask) << 16)), NFC_FLASH_ADDR0);
+ /* the row address */
+ raw_write(((raw_read(NFC_FLASH_ADDR8) & (mask << 16)) |
+ ((page_addr & (mask << 16)) >> 16)),
+ NFC_FLASH_ADDR8);
+ } else if (page_addr != -1) {
+ raw_write(page_addr, NFC_FLASH_ADDR0);
+ raw_write(0, NFC_FLASH_ADDR8);
+ }
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "AutoMode:the ADDR REGS value is (0x%x, 0x%x)\n",
+ raw_read(NFC_FLASH_ADDR0), raw_read(NFC_FLASH_ADDR8));
+#else
+
+ u32 page_mask = g_page_mask;
+
+ if (column != -1) {
+ send_addr(column & 0xFF, false);
+ if (IS_2K_PAGE_NAND) {
+ /* another col addr cycle for 2k page */
+ send_addr((column >> 8) & 0xF, false);
+ } else if (IS_4K_PAGE_NAND) {
+ /* another col addr cycle for 4k page */
+ send_addr((column >> 8) & 0x1F, false);
+ }
+ }
+ if (page_addr != -1) {
+ do {
+ send_addr((page_addr & 0xff), false);
+ page_mask >>= 8;
+ page_addr >>= 8;
+ } while (page_mask != 0);
+ }
+#endif
+}
+
+/*!
+ * This function is used by the upper layer to write command to NAND Flash for
+ * different operations to be carried out on NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param command command for NAND Flash
+ * @param column column offset for the page read
+ * @param page_addr page to be read from NAND Flash
+ */
+static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ bool useirq = false;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
+ command, column, page_addr);
+ /*
+ * Reset command state information
+ */
+ g_nandfc_info.bStatusRequest = false;
+
+ /*
+ * Command pre-processing step
+ */
+ switch (command) {
+ case NAND_CMD_STATUS:
+ g_nandfc_info.colAddr = 0;
+ g_nandfc_info.bStatusRequest = true;
+ break;
+
+ case NAND_CMD_READ0:
+ g_nandfc_info.colAddr = column;
+ break;
+
+ case NAND_CMD_READOOB:
+ g_nandfc_info.colAddr = column;
+ command = NAND_CMD_READ0;
+ break;
+
+ case NAND_CMD_SEQIN:
+ if (column != 0) {
+
+ /* FIXME: before send SEQIN command for
+ * partial write,We need read one page out.
+ * FSL NFC does not support partial write
+ * It alway send out 512+ecc+512+ecc ...
+ * for large page nand flash. But for small
+ * page nand flash, it did support SPARE
+ * ONLY operation. But to make driver
+ * simple. We take the same as large page,read
+ * whole page out and update. As for MLC nand
+ * NOP(num of operation) = 1. Partial written
+ * on one programed page is not allowed! We
+ * can't limit it on the driver, it need the
+ * upper layer applicaiton take care it
+ */
+
+ mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr);
+ }
+
+ g_nandfc_info.colAddr = column;
+ column = 0;
+
+ break;
+
+ case NAND_CMD_PAGEPROG:
+#ifndef NFC_AUTO_MODE_ENABLE
+ /* FIXME:the NFC interal buffer
+ * access has some limitation, it
+ * does not allow byte access. To
+ * make the code simple and ease use
+ * not every time check the address
+ * alignment.Use the temp buffer
+ * to accomadate the data.since We
+ * know data_buf will be at leat 4
+ * byte alignment, so we can use
+ * memcpy safely
+ */
+ nfc_memcpy(MAIN_AREA0, data_buf, mtd->writesize);
+ copy_spare(mtd, oob_buf, SPARE_AREA0, mtd->oobsize, false);
+#endif
+
+ if (IS_LARGE_PAGE_NAND)
+ PROG_PAGE();
+ else
+ send_prog_page(0);
+
+ useirq = true;
+
+ break;
+
+ case NAND_CMD_ERASE1:
+ break;
+ case NAND_CMD_ERASE2:
+ useirq = true;
+
+ break;
+ }
+
+ /*
+ * Write out the command to the device.
+ */
+ send_cmd(mtd, command, useirq);
+
+ mxc_do_addr_cycle(mtd, column, page_addr);
+
+ /*
+ * Command post-processing step
+ */
+ switch (command) {
+
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READ0:
+ if (IS_LARGE_PAGE_NAND) {
+ /* send read confirm command */
+ send_cmd(mtd, NAND_CMD_READSTART, false);
+ /* read for each AREA */
+ READ_PAGE();
+ } else {
+ send_read_page(0);
+ }
+
+#ifndef NFC_AUTO_MODE_ENABLE
+ /* FIXME, the NFC interal buffer
+ * access has some limitation, it
+ * does not allow byte access. To
+ * make the code simple and ease use
+ * not every time check the address
+ * alignment.Use the temp buffer
+ * to accomadate the data.since We
+ * know data_buf will be at leat 4
+ * byte alignment, so we can use
+ * memcpy safely
+ */
+ nfc_memcpy(data_buf, MAIN_AREA0, mtd->writesize);
+ copy_spare(mtd, oob_buf, SPARE_AREA0, mtd->oobsize, true);
+#endif
+
+ break;
+
+ case NAND_CMD_READID:
+ send_read_id();
+ g_nandfc_info.colAddr = column;
+ nfc_memcpy(data_buf, MAIN_AREA0, 2048);
+
+ break;
+ }
+}
+
+static int mxc_nand_read_oob(struct mtd_info *mtd,
+ struct nand_chip *chip, int page, int sndcmd)
+{
+ if (sndcmd) {
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ sndcmd = 0;
+ }
+
+ memcpy(chip->oob_poi, oob_buf, mtd->oobsize);
+
+ return sndcmd;
+}
+
+static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t * buf)
+{
+
+#ifndef NFC_AUTO_MODE_ENABLE
+ mxc_check_ecc_status(mtd);
+#endif
+
+ memcpy(buf, data_buf, mtd->writesize);
+ memcpy(chip->oob_poi, oob_buf, mtd->oobsize);
+
+ return 0;
+}
+
+static void mxc_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t * buf)
+{
+ memcpy(data_buf, buf, mtd->writesize);
+ memcpy(oob_buf, chip->oob_poi, mtd->oobsize);
+
+}
+
+/* Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks. */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 5,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_memorybased = {
+ .options = 0,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+static int mxc_nand_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ g_page_mask = this->pagemask;
+
+ /* limit to 2G size due to Kernel
+ * larger 4G space support,need fix
+ * it later
+ */
+ if (mtd->size == 0) {
+ mtd->size = 1 << 31;
+ this->numchips = 1;
+ this->chipsize = mtd->size;
+ }
+
+ if (IS_2K_PAGE_NAND) {
+ NFC_SET_NFMS(1 << NFMS_NF_PG_SZ);
+ this->ecc.layout = &nand_hw_eccoob_2k;
+ } else if (IS_4K_PAGE_NAND) {
+ NFC_SET_NFMS(1 << NFMS_NF_PG_SZ);
+ this->ecc.layout = &nand_hw_eccoob_4k;
+ } else {
+ this->ecc.layout = &nand_hw_eccoob_512;
+ }
+
+ /* propagate ecc.layout to mtd_info */
+ mtd->ecclayout = this->ecc.layout;
+
+ /* jffs2 not write oob */
+ mtd->flags &= ~MTD_OOB_WRITEABLE;
+
+ /* use flash based bbt */
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+
+ /* update flash based bbt */
+ this->options |= NAND_USE_FLASH_BBT;
+
+ if (!this->badblock_pattern) {
+ this->badblock_pattern = (mtd->writesize > 512) ?
+ &largepage_memorybased : &smallpage_memorybased;
+ }
+
+ /* Build bad block table */
+ return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+static void mxc_nfc_init(void)
+{
+ /* Disable interrupt */
+ raw_write((raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK), REG_NFC_INTRRUPT);
+
+ /* disable spare enable */
+ raw_write(raw_read(REG_NFC_SP_EN) & ~NFC_SP_EN, REG_NFC_SP_EN);
+
+ /* Unlock the internal RAM Buffer */
+ raw_write(NFC_SET_BLS(NFC_BLS_UNLCOKED), REG_NFC_BLS);
+
+ /* Blocks to be unlocked */
+ UNLOCK_ADDR(0x0, 0xFFFF);
+
+ /* Unlock Block Command for given address range */
+ raw_write(NFC_SET_WPC(NFC_WPC_UNLOCK), REG_NFC_WPC);
+
+ /* Enable symetric mode by default except mx37TO1.0 */
+ if (!(cpu_is_mx37_rev(CHIP_REV_1_0) == 1))
+ raw_write(raw_read(REG_NFC_ONE_CYCLE) |
+ NFC_ONE_CYCLE, REG_NFC_ONE_CYCLE);
+}
+
+static int mxc_alloc_buf(void)
+{
+ int err = 0;
+
+ data_buf = kzalloc(NAND_MAX_PAGESIZE, GFP_KERNEL);
+ if (!data_buf) {
+ printk(KERN_ERR "%s: failed to allocate data_buf\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+ oob_buf = kzalloc(NAND_MAX_OOBSIZE, GFP_KERNEL);
+ if (!oob_buf) {
+ printk(KERN_ERR "%s: failed to allocate oob_buf\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ out:
+ return err;
+}
+
+static void mxc_free_buf(void)
+{
+ kfree(data_buf);
+ kfree(oob_buf);
+}
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and
+ * remove functions
+ *
+ * @return The function always returns 0.
+ */
+static int __init mxcnd_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+ int nr_parts = 0, err = 0;
+
+ nfc_axi_base = IO_ADDRESS(NFC_AXI_BASE_ADDR);
+ nfc_ip_base = IO_ADDRESS(NFC_BASE_ADDR);
+
+ /* init the nfc */
+ mxc_nfc_init();
+
+ /* init data buf */
+ if (mxc_alloc_buf())
+ goto out;
+
+ /* Allocate memory for MTD device structure and private data */
+ mxc_nand_data = kzalloc(sizeof(struct mxc_mtd_s), GFP_KERNEL);
+ if (!mxc_nand_data) {
+ printk(KERN_ERR "%s: failed to allocate mtd_info\n",
+ __FUNCTION__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info));
+
+ mxc_nand_data->dev = &pdev->dev;
+ /* structures must be linked */
+ this = &mxc_nand_data->nand;
+ mtd = &mxc_nand_data->mtd;
+ mtd->priv = this;
+ mtd->owner = THIS_MODULE;
+
+ this->priv = mxc_nand_data;
+ this->cmdfunc = mxc_nand_command;
+ this->select_chip = mxc_nand_select_chip;
+ this->read_byte = mxc_nand_read_byte;
+ this->read_word = mxc_nand_read_word;
+ this->write_buf = mxc_nand_write_buf;
+ this->read_buf = mxc_nand_read_buf;
+ this->verify_buf = mxc_nand_verify_buf;
+ this->scan_bbt = mxc_nand_scan_bbt;
+
+ /* NAND bus width determines access funtions used by upper layer */
+ if (flash->width == 2) {
+ this->read_byte = mxc_nand_read_byte16;
+ this->options |= NAND_BUSWIDTH_16;
+ NFC_SET_NFMS(1 << NFMS_NF_DWIDTH);
+ } else {
+ NFC_SET_NFMS(0);
+ }
+
+ nfc_clk = clk_get(&pdev->dev, "nfc_clk");
+ clk_enable(nfc_clk);
+
+ init_waitqueue_head(&irq_waitq);
+ err = request_irq(MXC_INT_NANDFC, mxc_nfc_irq, 0, "mxc_nd", NULL);
+ if (err) {
+ goto out_1;
+ }
+
+ if (hardware_ecc) {
+ this->ecc.read_page = mxc_nand_read_page;
+ this->ecc.write_page = mxc_nand_write_page;
+ this->ecc.read_oob = mxc_nand_read_oob;
+ this->ecc.layout = &nand_hw_eccoob_512;
+ this->ecc.calculate = mxc_nand_calculate_ecc;
+ this->ecc.hwctl = mxc_nand_enable_hwecc;
+ this->ecc.correct = mxc_nand_correct_data;
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.size = 512;
+ this->ecc.bytes = 9;
+ raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN),
+ REG_NFC_ECC_EN);
+ } else {
+ this->ecc.mode = NAND_ECC_SOFT;
+ raw_write((raw_read(REG_NFC_ECC_EN) & ~NFC_ECC_EN),
+ REG_NFC_ECC_EN);
+ }
+
+ /* config the gpio */
+ if (flash->init)
+ flash->init();
+
+ /* Reset NAND */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* Scan to find existence of the device */
+ if (nand_scan(mtd, NFC_GET_MAXCHIP_SP())) {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "MXC_ND2: Unable to find any NAND device.\n");
+ err = -ENXIO;
+ goto out_1;
+ }
+
+ /* Register the partitions */
+#ifdef CONFIG_MTD_PARTITIONS
+ nr_parts =
+ parse_mtd_partitions(mtd, part_probes, &mxc_nand_data->parts, 0);
+ if (nr_parts > 0)
+ add_mtd_partitions(mtd, mxc_nand_data->parts, nr_parts);
+ else if (flash->parts)
+ add_mtd_partitions(mtd, flash->parts, flash->nr_parts);
+ else
+#endif
+ {
+ pr_info("Registering %s as whole device\n", mtd->name);
+ add_mtd_device(mtd);
+ }
+
+ platform_set_drvdata(pdev, mtd);
+
+ return 0;
+
+ out_1:
+ kfree(mxc_nand_data);
+ out:
+ return err;
+
+}
+
+ /*!
+ * Dissociates the driver from the device.
+ *
+ * @param pdev the device structure used to give information on which
+ *
+ * @return The function always returns 0.
+ */
+
+static int __exit mxcnd_remove(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+
+ if (flash->exit)
+ flash->exit();
+
+ mxc_free_buf();
+
+ clk_disable(nfc_clk);
+ clk_put(nfc_clk);
+ platform_set_drvdata(pdev, NULL);
+
+ if (mxc_nand_data) {
+ nand_release(mtd);
+ free_irq(MXC_INT_NANDFC, NULL);
+ kfree(mxc_nand_data);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function is called to put the NAND in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device information structure
+ *
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+
+static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND2 : NAND suspend\n");
+ if (info)
+ ret = info->suspend(info);
+
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+
+ return ret;
+}
+
+/*!
+ * This function is called to bring the NAND back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device information structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxcnd_resume(struct platform_device *pdev)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND2 : NAND resume\n");
+ /* Enable the NFC clock */
+ clk_enable(nfc_clk);
+
+ if (info) {
+ info->resume(info);
+ }
+
+ return ret;
+}
+
+#else
+#define mxcnd_suspend NULL
+#define mxcnd_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcnd_driver = {
+ .driver = {
+ .name = "mxc_nandv2_flash",
+ },
+ .probe = mxcnd_probe,
+ .remove = __exit_p(mxcnd_remove),
+ .suspend = mxcnd_suspend,
+ .resume = mxcnd_resume,
+};
+
+/*!
+ * Main initialization routine
+ * @return 0 if successful; non-zero otherwise
+ */
+static int __init mxc_nd_init(void)
+{
+ /* Register the device driver structure. */
+ pr_info("MXC MTD nand Driver %s\n", DVR_VER);
+ if (platform_driver_register(&mxcnd_driver) != 0) {
+ printk(KERN_ERR "Driver register failed for mxcnd_driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/*!
+ * Clean up routine
+ */
+static void __exit mxc_nd_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mxcnd_driver);
+}
+
+module_init(mxc_nd_init);
+module_exit(mxc_nd_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC NAND MTD driver Version 2-5");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/mxc_nd2.h b/drivers/mtd/nand/mxc_nd2.h
new file mode 100644
index 000000000000..9cb92100b5a4
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd2.h
@@ -0,0 +1,679 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxc_nd2.h
+ *
+ * @brief This file contains the NAND Flash Controller register information.
+ *
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifndef __MXC_ND2_H__
+#define __MXC_ND2_H__
+
+#include <mach/hardware.h>
+
+#define IS_2K_PAGE_NAND ((mtd->writesize / num_of_interleave) \
+ == NAND_PAGESIZE_2KB)
+#define IS_4K_PAGE_NAND ((mtd->writesize / num_of_interleave) \
+ == NAND_PAGESIZE_4KB)
+#define IS_LARGE_PAGE_NAND ((mtd->writesize / num_of_interleave) > 512)
+
+#define GET_NAND_OOB_SIZE (mtd->oobsize / num_of_interleave)
+
+#define NAND_PAGESIZE_2KB 2048
+#define NAND_PAGESIZE_4KB 4096
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3
+/*
+ * For V3 NFC registers Definition
+ */
+/* AXI Bus Mapped */
+#define NFC_AXI_BASE_ADDR NFC_BASE_ADDR_AXI
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) /* mx37 */
+#define MXC_INT_NANDFC MXC_INT_EMI
+#define NFC_FLASH_ADDR_CMD (nfc_axi_base + 0x1E00)
+#define NFC_CONFIG1 (nfc_axi_base + 0x1E04)
+#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0x1E08)
+#define LAUNCH_NFC (nfc_axi_base + 0x1E0c)
+#define NFC_WRPROT (nfc_ip_base + 0x00)
+#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04)
+#define NFC_CONFIG2 (nfc_ip_base + 0x14)
+#define NFC_IPC (nfc_ip_base + 0x18)
+#elif defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */
+#define MXC_INT_NANDFC MXC_INT_NFC
+#define NFC_AUTO_MODE_ENABLE
+#define NFC_FLASH_CMD (nfc_axi_base + 0x1E00)
+#define NFC_FLASH_ADDR0 (nfc_axi_base + 0x1E04)
+#define NFC_FLASH_ADDR8 (nfc_axi_base + 0x1E24)
+#define NFC_CONFIG1 (nfc_axi_base + 0x1E34)
+#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0x1E38)
+#define NFC_ECC_STATUS_SUM (nfc_axi_base + 0x1E3C)
+#define LAUNCH_NFC (nfc_axi_base + 0x1E40)
+#define NFC_WRPROT (nfc_ip_base + 0x00)
+#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04)
+#define NFC_CONFIG2 (nfc_ip_base + 0x24)
+#define NFC_CONFIG3 (nfc_ip_base + 0x28)
+#define NFC_IPC (nfc_ip_base + 0x2C)
+#define NFC_DELAY_LINE (nfc_ip_base + 0x34)
+#else /* skye */
+#define NFC_FLASH_ADDR_CMD (nfc_axi_base + 0xE00)
+#define NFC_CONFIG1 (nfc_axi_base + 0xE04)
+#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0xE08)
+#define LAUNCH_NFC (nfc_axi_base + 0xE0C)
+#define NFC_WRPROT (nfc_ip_base + 0x00)
+#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04)
+#define NFC_CONFIG2 (nfc_ip_base + 0x14)
+#define NFC_IPC (nfc_ip_base + 0x18)
+#endif
+/*!
+ * Addresses for NFC RAM BUFFER Main area 0
+ */
+#define MAIN_AREA0 ((u16 *)(nfc_axi_base + 0x000))
+#define MAIN_AREA1 ((u16 *)(nfc_axi_base + 0x200))
+
+/*!
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \
+ defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define SPARE_AREA0 ((u16 *)(nfc_axi_base + 0x1000))
+#define SPARE_LEN 64
+#define SPARE_COUNT 8
+#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT)
+#else
+#define SPARE_AREA0 ((u16 *)(nfc_axi_base + 0x800))
+#define SPARE_LEN 16
+#define SPARE_COUNT 4
+#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT)
+#endif
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \
+ defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define NFC_SPAS_WIDTH 8
+#define NFC_SPAS_SHIFT 16
+
+#define IS_4BIT_ECC \
+( \
+ cpu_is_mx51_rev(CHIP_REV_2_0) > 0 ? \
+ !((raw_read(NFC_CONFIG2) & NFC_ECC_MODE_4) >> 6) : \
+ ((raw_read(NFC_CONFIG2) & NFC_ECC_MODE_4) >> 6) \
+)
+
+#define NFC_SET_SPAS(v) \
+ raw_write((((raw_read(NFC_CONFIG2) & \
+ NFC_FIELD_RESET(NFC_SPAS_WIDTH, NFC_SPAS_SHIFT)) | ((v) << 16))), \
+ NFC_CONFIG2)
+
+#define NFC_SET_ECC_MODE(v) \
+do { \
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) > 0) { \
+ if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ NFC_ECC_MODE_MASK) | \
+ NFC_ECC_MODE_4), NFC_CONFIG2); \
+ else \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ NFC_ECC_MODE_MASK) & \
+ NFC_ECC_MODE_8), NFC_CONFIG2); \
+ } else { \
+ if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ NFC_ECC_MODE_MASK) & \
+ NFC_ECC_MODE_8), NFC_CONFIG2); \
+ else \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ NFC_ECC_MODE_MASK) | \
+ NFC_ECC_MODE_4), NFC_CONFIG2); \
+ } \
+} while (0)
+
+#define WRITE_NFC_IP_REG(val,reg) \
+ do { \
+ raw_write(NFC_IPC_CREQ, NFC_IPC); \
+ while (!((raw_read(NFC_IPC) & NFC_IPC_ACK)>>1));\
+ raw_write(val, reg); \
+ raw_write(0, NFC_IPC); \
+ } while(0)
+
+#else
+#define IS_4BIT_ECC 1
+#define NFC_SET_SPAS(v)
+#define NFC_SET_ECC_MODE(v)
+#define NFC_SET_NFMS(v) (NFMS |= (v))
+
+#define WRITE_NFC_IP_REG(val,reg) \
+ raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \
+ REG_NFC_OPS_STAT)
+#endif
+
+#define GET_NFC_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT);
+
+/*!
+ * Set 1 to specific operation bit, rest to 0 in LAUNCH_NFC Register for
+ * Specific operation
+ */
+#define NFC_CMD 0x1
+#define NFC_ADDR 0x2
+#define NFC_INPUT 0x4
+#define NFC_OUTPUT 0x8
+#define NFC_ID 0x10
+#define NFC_STATUS 0x20
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */
+#define NFC_AUTO_PROG 0x40
+#define NFC_AUTO_READ 0x80
+#define NFC_AUTO_ERASE 0x200
+#define NFC_COPY_BACK_0 0x400
+#define NFC_COPY_BACK_1 0x800
+#define NFC_AUTO_STATE 0x1000
+#endif
+
+/* Bit Definitions for NFC_IPC*/
+#define NFC_OPS_STAT (1 << 31)
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */
+#define NFC_OP_DONE (1 << 30)
+#define NFC_RB (1 << 28)
+#define NFC_PS_WIDTH 2
+#define NFC_PS_SHIFT 0
+#define NFC_PS_512 0
+#define NFC_PS_2K 1
+#define NFC_PS_4K 2
+#else
+#define NFC_RB (1 << 29)
+#endif
+
+#define NFC_ONE_CYCLE (1 << 2)
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */
+#define NFC_INT_MSK (1 << 15)
+#define NFC_AUTO_PROG_DONE_MSK (1 << 14)
+#define NFC_NUM_ADDR_PHASE1_WIDTH 2
+#define NFC_NUM_ADDR_PHASE1_SHIFT 12
+
+#define NFC_NUM_ADDR_PHASE0_WIDTH 1
+#define NFC_NUM_ADDR_PHASE0_SHIFT 5
+
+#define NFC_ONE_LESS_PHASE1 0
+#define NFC_TWO_LESS_PHASE1 1
+
+#define NFC_FLASH_ADDR_SHIFT 0
+#else
+#define NFC_INT_MSK (1 << 4)
+#define NFC_BIG (1 << 5)
+#define NFC_FLASH_ADDR_SHIFT 16
+#endif
+
+#define NFC_UNLOCK_END_ADDR_SHIFT 16
+
+/* Bit definition for NFC_CONFIGRATION_1 */
+#define NFC_SP_EN (1 << 0)
+#define NFC_CE (1 << 1)
+#define NFC_RST (1 << 2)
+#define NFC_ECC_EN (1 << 3)
+
+#define NFC_FIELD_RESET(width, shift) ~(((1 << (width)) - 1) << (shift))
+
+#define NFC_RBA_SHIFT 4
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \
+ defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */
+#define NFC_RBA_WIDTH 3
+#else
+#define NFC_RBA_WIDTH 2
+#endif
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */
+#define NFC_ITERATION_SHIFT 8
+#define NFC_ITERATION_WIDTH 4
+#define NFC_ACTIVE_CS_SHIFT 12
+#define NFC_ACTIVE_CS_WIDTH 3
+/* bit definition for CONFIGRATION3 */
+#define NFC_NO_SDMA (1 << 20)
+#define NFC_FMP_SHIFT 16
+#define NFC_FMP_WIDTH 4
+#define NFC_RBB_MODE (1 << 15)
+#define NFC_NUM_OF_DEVICES_SHIFT 12
+#define NFC_NUM_OF_DEVICES_WIDTH 4
+#define NFC_DMA_MODE_SHIFT 11
+#define NFC_DMA_MODE_WIDTH 1
+#define NFC_SBB_SHIFT 8
+#define NFC_SBB_WIDTH 3
+#define NFC_BIG (1 << 7)
+#define NFC_SB2R_SHIFT 4
+#define NFC_SB2R_WIDTH 3
+#define NFC_FW_SHIFT 3
+#define NFC_FW_WIDTH 1
+#define NFC_TOO (1 << 2)
+#define NFC_ADD_OP_SHIFT 0
+#define NFC_ADD_OP_WIDTH 2
+#define NFC_FW_8 1
+#define NFC_FW_16 0
+#define NFC_ST_CMD_SHITF 24
+#define NFC_ST_CMD_WIDTH 8
+#endif
+
+#define NFC_PPB_32 (0 << 7)
+#define NFC_PPB_64 (1 << 7)
+#define NFC_PPB_128 (2 << 7)
+#define NFC_PPB_256 (3 << 7)
+#define NFC_PPB_RESET ~(3 << 7)
+
+#define NFC_BLS_LOCKED (0 << 16)
+#define NFC_BLS_LOCKED_DEFAULT (1 << 16)
+#define NFC_BLS_UNLCOKED (2 << 16)
+#define NFC_BLS_RESET ~(3 << 16)
+#define NFC_WPC_LOCK_TIGHT 1
+#define NFC_WPC_LOCK (1 << 1)
+#define NFC_WPC_UNLOCK (1 << 2)
+#define NFC_WPC_RESET ~(7)
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \
+ defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define NFC_ECC_MODE_4 (1 << 6)
+#define NFC_ECC_MODE_8 ~(1 << 6)
+#define NFC_ECC_MODE_MASK ~(1 << 6)
+#define NFC_SPAS_16 8
+#define NFC_SPAS_64 32
+#define NFC_SPAS_128 64
+#define NFC_SPAS_112 56
+#define NFC_SPAS_218 109
+#define NFC_IPC_CREQ (1 << 0)
+#define NFC_IPC_ACK (1 << 1)
+#endif
+
+#define REG_NFC_OPS_STAT NFC_IPC
+#define REG_NFC_INTRRUPT NFC_CONFIG2
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2
+#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR0
+#define REG_NFC_FLASH_CMD NFC_FLASH_CMD
+#else
+#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR_CMD
+#define REG_NFC_FLASH_CMD NFC_FLASH_ADDR_CMD
+#endif
+#define REG_NFC_OPS LAUNCH_NFC
+#define REG_NFC_SET_RBA NFC_CONFIG1
+#define REG_NFC_RB NFC_IPC
+#define REG_NFC_ECC_EN NFC_CONFIG2
+#define REG_NFC_ECC_STATUS_RESULT NFC_ECC_STATUS_RESULT
+#define REG_NFC_CE NFC_CONFIG1
+#define REG_NFC_RST NFC_CONFIG1
+#define REG_NFC_PPB NFC_CONFIG2
+#define REG_NFC_SP_EN NFC_CONFIG1
+#define REG_NFC_BLS NFC_WRPROT
+#define REG_UNLOCK_BLK_ADD0 NFC_WRPROT_UNLOCK_BLK_ADD0
+#define REG_UNLOCK_BLK_ADD1 NFC_WRPROT_UNLOCK_BLK_ADD1
+#define REG_UNLOCK_BLK_ADD2 NFC_WRPROT_UNLOCK_BLK_ADD2
+#define REG_UNLOCK_BLK_ADD3 NFC_WRPROT_UNLOCK_BLK_ADD3
+#define REG_NFC_WPC NFC_WRPROT
+#define REG_NFC_ONE_CYCLE NFC_CONFIG2
+
+/* NFC V3 Specific MACRO functions definitions */
+#define raw_write(v,a) __raw_writel(v,a)
+#define raw_read(a) __raw_readl(a)
+
+/* Explcit ack ops status (if any), before issue of any command */
+#define ACK_OPS \
+ raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \
+ REG_NFC_OPS_STAT);
+
+/* Set RBA buffer id*/
+#define NFC_SET_RBA(val) \
+ raw_write((raw_read(REG_NFC_SET_RBA) & \
+ (NFC_FIELD_RESET(NFC_RBA_WIDTH, NFC_RBA_SHIFT))) | \
+ ((val) << NFC_RBA_SHIFT), REG_NFC_SET_RBA);
+
+#define NFC_SET_PS(val) \
+ raw_write((raw_read(NFC_CONFIG2) & \
+ (NFC_FIELD_RESET(NFC_PS_WIDTH, NFC_PS_SHIFT))) | \
+ ((val) << NFC_PS_SHIFT), NFC_CONFIG2);
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2
+#define UNLOCK_ADDR(start_addr,end_addr) \
+{ \
+ int i = 0; \
+ for (; i < NAND_MAX_CHIPS; i++) \
+ raw_write(start_addr | \
+ (end_addr << NFC_UNLOCK_END_ADDR_SHIFT), \
+ REG_UNLOCK_BLK_ADD0 + (i << 2)); \
+}
+#define NFC_SET_NFC_ACTIVE_CS(val) \
+ raw_write((raw_read(NFC_CONFIG1) & \
+ (NFC_FIELD_RESET(NFC_ACTIVE_CS_WIDTH, NFC_ACTIVE_CS_SHIFT))) | \
+ ((val) << NFC_ACTIVE_CS_SHIFT), NFC_CONFIG1);
+
+#define NFC_GET_MAXCHIP_SP() 8
+
+#else
+#define UNLOCK_ADDR(start_addr,end_addr) \
+ raw_write(start_addr | \
+ (end_addr << NFC_UNLOCK_END_ADDR_SHIFT), REG_UNLOCK_BLK_ADD0);
+
+#define NFC_SET_NFC_ACTIVE_CS(val)
+#define NFC_GET_MAXCHIP_SP() 1
+#endif
+
+#define NFC_SET_BLS(val) ((raw_read(REG_NFC_BLS) & NFC_BLS_RESET) | val )
+#define NFC_SET_WPC(val) ((raw_read(REG_NFC_WPC) & NFC_WPC_RESET) | val )
+#define CHECK_NFC_RB raw_read(REG_NFC_RB) & NFC_RB
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define NFC_SET_NFC_NUM_ADDR_PHASE1(val) \
+ raw_write((raw_read(NFC_CONFIG2) & \
+ (NFC_FIELD_RESET(NFC_NUM_ADDR_PHASE1_WIDTH, \
+ NFC_NUM_ADDR_PHASE1_SHIFT))) | \
+ ((val) << NFC_NUM_ADDR_PHASE1_SHIFT), NFC_CONFIG2);
+
+#define NFC_SET_NFC_NUM_ADDR_PHASE0(val) \
+ raw_write((raw_read(NFC_CONFIG2) & \
+ (NFC_FIELD_RESET(NFC_NUM_ADDR_PHASE0_WIDTH, \
+ NFC_NUM_ADDR_PHASE0_SHIFT))) | \
+ ((val) << NFC_NUM_ADDR_PHASE0_SHIFT), NFC_CONFIG2);
+
+#define NFC_SET_NFC_ITERATION(val) \
+ raw_write((raw_read(NFC_CONFIG1) & \
+ (NFC_FIELD_RESET(NFC_ITERATION_WIDTH, NFC_ITERATION_SHIFT))) | \
+ ((val) << NFC_ITERATION_SHIFT), NFC_CONFIG1);
+
+#define NFC_SET_FW(val) \
+ raw_write((raw_read(NFC_CONFIG3) & \
+ (NFC_FIELD_RESET(NFC_FW_WIDTH, NFC_FW_SHIFT))) | \
+ ((val) << NFC_FW_SHIFT), NFC_CONFIG3);
+
+#define NFC_SET_NUM_OF_DEVICE(val) \
+ raw_write((raw_read(NFC_CONFIG3) & \
+ (NFC_FIELD_RESET(NFC_NUM_OF_DEVICES_WIDTH, \
+ NFC_NUM_OF_DEVICES_SHIFT))) | \
+ ((val) << NFC_NUM_OF_DEVICES_SHIFT), NFC_CONFIG3);
+
+#define NFC_SET_ADD_OP_MODE(val) \
+ raw_write((raw_read(NFC_CONFIG3) & \
+ (NFC_FIELD_RESET(NFC_ADD_OP_WIDTH, NFC_ADD_OP_SHIFT))) | \
+ ((val) << NFC_ADD_OP_SHIFT), NFC_CONFIG3);
+
+#define NFC_SET_ADD_CS_MODE(val) \
+{ \
+ NFC_SET_ADD_OP_MODE(val); \
+ NFC_SET_NUM_OF_DEVICE(this->numchips - 1); \
+}
+
+#define NFC_SET_ST_CMD(val) \
+ raw_write((raw_read(NFC_CONFIG2) & \
+ (NFC_FIELD_RESET(NFC_ST_CMD_WIDTH, \
+ NFC_ST_CMD_SHITF))) | \
+ ((val) << NFC_ST_CMD_SHITF), NFC_CONFIG2);
+
+#define NFMS_NF_DWIDTH 0
+#define NFMS_NF_PG_SZ 1
+#define NFC_CMD_1_SHIFT 8
+
+#define NUM_OF_ADDR_CYCLE (fls(g_page_mask) >> 3)
+#define SET_NFC_DELAY_LINE(val) raw_write((val), NFC_DELAY_LINE)
+
+/*should set the fw,ps,spas,ppb*/
+#define NFC_SET_NFMS(v) \
+do { \
+ if (!(v)) \
+ NFC_SET_FW(NFC_FW_8); \
+ if (((v) & (1 << NFMS_NF_DWIDTH))) \
+ NFC_SET_FW(NFC_FW_16); \
+ if (((v) & (1 << NFMS_NF_PG_SZ))) { \
+ if (IS_2K_PAGE_NAND) { \
+ NFC_SET_PS(NFC_PS_2K); \
+ NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE); \
+ NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_TWO_LESS_PHASE1); \
+ } else if (IS_4K_PAGE_NAND) { \
+ NFC_SET_PS(NFC_PS_4K); \
+ NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE); \
+ NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_TWO_LESS_PHASE1); \
+ } else { \
+ NFC_SET_PS(NFC_PS_512); \
+ NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE - 1); \
+ NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_ONE_LESS_PHASE1); \
+ } \
+ NFC_SET_ADD_CS_MODE(1); \
+ NFC_SET_SPAS(GET_NAND_OOB_SIZE >> 1); \
+ NFC_SET_ECC_MODE(GET_NAND_OOB_SIZE >> 1); \
+ NFC_SET_ST_CMD(0x70); \
+ raw_write(raw_read(NFC_CONFIG3) | 1 << 20, NFC_CONFIG3); \
+ SET_NFC_DELAY_LINE(0); \
+ } \
+} while (0)
+#endif
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_1
+#define NFC_SET_NFMS(v)
+#endif
+
+#define READ_PAGE() send_read_page(0)
+#define PROG_PAGE() send_prog_page(0)
+
+#elif CONFIG_ARCH_MXC_HAS_NFC_V2
+
+/*
+ * For V1/V2 NFC registers Definition
+ */
+
+#define NFC_AXI_BASE_ADDR 0x00
+/*
+ * Addresses for NFC registers
+ */
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_REG_BASE (nfc_ip_base + 0x1000)
+#else
+#define NFC_REG_BASE nfc_ip_base
+#endif
+#define NFC_BUF_SIZE (NFC_REG_BASE + 0xE00)
+#define NFC_BUF_ADDR (NFC_REG_BASE + 0xE04)
+#define NFC_FLASH_ADDR (NFC_REG_BASE + 0xE06)
+#define NFC_FLASH_CMD (NFC_REG_BASE + 0xE08)
+#define NFC_CONFIG (NFC_REG_BASE + 0xE0A)
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_ECC_STATUS_RESULT (NFC_REG_BASE + 0xE0C)
+#define NFC_ECC_STATUS_RESULT_1 (NFC_REG_BASE + 0xE0C)
+#define NFC_ECC_STATUS_RESULT_2 (NFC_REG_BASE + 0xE0E)
+#define NFC_SPAS (NFC_REG_BASE + 0xE10)
+#else
+#define NFC_ECC_STATUS_RESULT (NFC_REG_BASE + 0xE0C)
+#define NFC_RSLTMAIN_AREA (NFC_REG_BASE + 0xE0E)
+#define NFC_RSLTSPARE_AREA (NFC_REG_BASE + 0xE10)
+#endif
+#define NFC_WRPROT (NFC_REG_BASE + 0xE12)
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_UNLOCKSTART_BLKADDR (NFC_REG_BASE + 0xE20)
+#define NFC_UNLOCKEND_BLKADDR (NFC_REG_BASE + 0xE22)
+#define NFC_UNLOCKSTART_BLKADDR1 (NFC_REG_BASE + 0xE24)
+#define NFC_UNLOCKEND_BLKADDR1 (NFC_REG_BASE + 0xE26)
+#define NFC_UNLOCKSTART_BLKADDR2 (NFC_REG_BASE + 0xE28)
+#define NFC_UNLOCKEND_BLKADDR2 (NFC_REG_BASE + 0xE2A)
+#define NFC_UNLOCKSTART_BLKADDR3 (NFC_REG_BASE + 0xE2C)
+#define NFC_UNLOCKEND_BLKADDR3 (NFC_REG_BASE + 0xE2E)
+#else
+#define NFC_UNLOCKSTART_BLKADDR (NFC_REG_BASE + 0xE14)
+#define NFC_UNLOCKEND_BLKADDR (NFC_REG_BASE + 0xE16)
+#endif
+#define NFC_NF_WRPRST (NFC_REG_BASE + 0xE18)
+#define NFC_CONFIG1 (NFC_REG_BASE + 0xE1A)
+#define NFC_CONFIG2 (NFC_REG_BASE + 0xE1C)
+
+/*!
+ * Addresses for NFC RAM BUFFER Main area 0
+ */
+#define MAIN_AREA0 (u16 *)(nfc_ip_base + 0x000)
+#define MAIN_AREA1 (u16 *)(nfc_ip_base + 0x200)
+
+/*!
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define SPARE_AREA0 (u16 *)(nfc_ip_base + 0x1000)
+#define SPARE_LEN 64
+#define SPARE_COUNT 8
+#else
+#define SPARE_AREA0 (u16 *)(nfc_ip_base + 0x800)
+#define SPARE_LEN 16
+#define SPARE_COUNT 4
+#endif
+#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT)
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define REG_NFC_ECC_MODE NFC_CONFIG1
+#define SPAS_SHIFT (0)
+#define REG_NFC_SPAS NFC_SPAS
+#define SPAS_MASK (0xFF00)
+#define IS_4BIT_ECC \
+ ((raw_read(REG_NFC_ECC_MODE) & NFC_ECC_MODE_4) >> 0)
+
+#define NFC_SET_SPAS(v) \
+ raw_write(((raw_read(REG_NFC_SPAS) & SPAS_MASK) | ((v<<SPAS_SHIFT))), \
+ REG_NFC_SPAS)
+
+#define NFC_SET_ECC_MODE(v) \
+do { \
+ if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) { \
+ raw_write((raw_read(REG_NFC_ECC_MODE) & NFC_ECC_MODE_8), \
+ REG_NFC_ECC_MODE); \
+ } else { \
+ raw_write((raw_read(REG_NFC_ECC_MODE) | NFC_ECC_MODE_4), \
+ REG_NFC_ECC_MODE); \
+ } \
+} while (0)
+
+#define GET_ECC_STATUS() __raw_readl(REG_NFC_ECC_STATUS_RESULT);
+#define NFC_SET_NFMS(v) \
+do { \
+ (NFMS |= (v)); \
+ if (((v) & (1 << NFMS_NF_PG_SZ))) { \
+ NFC_SET_SPAS(GET_NAND_OOB_SIZE >> 1); \
+ NFC_SET_ECC_MODE(GET_NAND_OOB_SIZE >> 1); \
+ } \
+} while (0)
+#else
+#define IS_4BIT_ECC (1)
+#define NFC_SET_SPAS(v)
+#define NFC_SET_ECC_MODE(v)
+#define GET_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT);
+#define NFC_SET_NFMS(v) (NFMS |= (v))
+#endif
+
+#define WRITE_NFC_IP_REG(val,reg) \
+ raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \
+ REG_NFC_OPS_STAT)
+
+#define GET_NFC_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT);
+
+/*!
+ * Set INT to 0, Set 1 to specific operation bit, rest to 0 in LAUNCH_NFC Register for
+ * Specific operation
+ */
+#define NFC_CMD 0x1
+#define NFC_ADDR 0x2
+#define NFC_INPUT 0x4
+#define NFC_OUTPUT 0x8
+#define NFC_ID 0x10
+#define NFC_STATUS 0x20
+
+/* Bit Definitions */
+#define NFC_OPS_STAT (1 << 15)
+#define NFC_SP_EN (1 << 2)
+#define NFC_ECC_EN (1 << 3)
+#define NFC_INT_MSK (1 << 4)
+#define NFC_BIG (1 << 5)
+#define NFC_RST (1 << 6)
+#define NFC_CE (1 << 7)
+#define NFC_ONE_CYCLE (1 << 8)
+#define NFC_BLS_LOCKED 0
+#define NFC_BLS_LOCKED_DEFAULT 1
+#define NFC_BLS_UNLCOKED 2
+#define NFC_WPC_LOCK_TIGHT 1
+#define NFC_WPC_LOCK (1 << 1)
+#define NFC_WPC_UNLOCK (1 << 2)
+#define NFC_FLASH_ADDR_SHIFT 0
+#define NFC_UNLOCK_END_ADDR_SHIFT 0
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_ECC_MODE_4 (1<<0)
+#define NFC_ECC_MODE_8 ~(1<<0)
+#define NFC_SPAS_16 8
+#define NFC_SPAS_64 32
+#define NFC_SPAS_112 56
+#define NFC_SPAS_128 64
+#define NFC_SPAS_218 109
+#endif
+/* NFC Register Mapping */
+#define REG_NFC_OPS_STAT NFC_CONFIG2
+#define REG_NFC_INTRRUPT NFC_CONFIG1
+#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR
+#define REG_NFC_FLASH_CMD NFC_FLASH_CMD
+#define REG_NFC_OPS NFC_CONFIG2
+#define REG_NFC_SET_RBA NFC_BUF_ADDR
+#define REG_NFC_ECC_EN NFC_CONFIG1
+#define REG_NFC_ECC_STATUS_RESULT NFC_ECC_STATUS_RESULT
+#define REG_NFC_CE NFC_CONFIG1
+#define REG_NFC_SP_EN NFC_CONFIG1
+#define REG_NFC_BLS NFC_CONFIG
+#define REG_NFC_WPC NFC_WRPROT
+#define REG_START_BLKADDR NFC_UNLOCKSTART_BLKADDR
+#define REG_END_BLKADDR NFC_UNLOCKEND_BLKADDR
+#define REG_NFC_RST NFC_CONFIG1
+#define REG_NFC_ONE_CYCLE NFC_CONFIG1
+
+/* NFC V1/V2 Specific MACRO functions definitions */
+
+#define raw_write(v,a) __raw_writew(v,a)
+#define raw_read(a) __raw_readw(a)
+
+#define NFC_SET_BLS(val) val
+
+#define UNLOCK_ADDR(start_addr,end_addr) \
+{ \
+ raw_write(start_addr,REG_START_BLKADDR); \
+ raw_write(end_addr,REG_END_BLKADDR); \
+}
+
+#define NFC_SET_NFC_ACTIVE_CS(val)
+#define NFC_GET_MAXCHIP_SP() 1
+#define NFC_SET_WPC(val) val
+
+/* NULL Definitions */
+#define ACK_OPS
+#define NFC_SET_RBA(val) raw_write(val, REG_NFC_SET_RBA);
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define READ_PAGE() send_read_page(0)
+#define PROG_PAGE() send_prog_page(0)
+#else
+#define READ_PAGE() \
+do { \
+ send_read_page(0); \
+ send_read_page(1); \
+ send_read_page(2); \
+ send_read_page(3); \
+} while (0)
+
+#define PROG_PAGE() \
+do { \
+ send_prog_page(0); \
+ send_prog_page(1); \
+ send_prog_page(2); \
+ send_prog_page(3); \
+} while (0)
+#endif
+#define CHECK_NFC_RB 1
+
+#endif
+
+#endif /* __MXC_ND2_H__ */