summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/Kconfig8
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/devices/Kconfig8
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/mxc_dataflash.c1037
-rw-r--r--drivers/mtd/maps/Kconfig10
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/mxc_nor.c181
-rw-r--r--drivers/mtd/mtd_blkdevs.c5
-rw-r--r--drivers/mtd/mtdchar.c1
-rw-r--r--drivers/mtd/nand/Kconfig62
-rw-r--r--drivers/mtd/nand/Makefile4
-rw-r--r--drivers/mtd/nand/gpmi-nfc/Makefile10
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v0.h550
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v1.h557
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v2.h567
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v0.h416
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h421
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v2.h511
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c898
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c664
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c625
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c918
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c1889
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c1929
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-common.c59
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c330
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v1.c80
-rw-r--r--drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h588
-rw-r--r--drivers/mtd/nand/imx_nfc.c8286
-rw-r--r--drivers/mtd/nand/mxc_nand.c3
-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.c1756
-rw-r--r--drivers/mtd/nand/mxc_nd2.h718
-rw-r--r--drivers/mtd/nand/nand_base.c19
-rw-r--r--drivers/mtd/nand/nand_device_info.c2413
-rw-r--r--drivers/mtd/nand/nand_device_info.h152
-rw-r--r--drivers/mtd/nand/nand_ids.c7
-rw-r--r--drivers/mtd/ubiblock.c626
40 files changed, 27830 insertions, 6 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index f8210bf2d241..20224423d617 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -342,4 +342,12 @@ source "drivers/mtd/lpddr/Kconfig"
source "drivers/mtd/ubi/Kconfig"
+config MTD_UBI_BLOCK
+ tristate "Simple mtdblock-like FTL over UBI volumes"
+ depends on MTD_UBI
+ select MTD_BLKDEVS
+ default n
+ help
+ This enables cached block access to UBI volumes
+
endif # MTD
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 760abc533395..d3fbad6e434e 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -33,3 +33,4 @@ inftl-objs := inftlcore.o inftlmount.o
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
obj-$(CONFIG_MTD_UBI) += ubi/
+obj-$(CONFIG_MTD_UBI_BLOCK) += ubiblock.o
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 35081ce77fbd..5079ad7553f0 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -78,6 +78,14 @@ config MTD_DATAFLASH_OTP
other key product data. The second half is programmed with a
unique-to-each-chip bit pattern at the factory.
+config MTD_MXC_DATAFLASH
+ tristate "Support for AT DataFlash via FSL SPI interface"
+ depends on SPI_MASTER && EXPERIMENTAL
+ help
+ This enables access to AT DataFlash chips, using FSL SPI.
+ Sometimes DataFlash chips are packaged inside MMC-format
+ cards; at this writing, the MMC stack won't handle those.
+
config MTD_M25P80
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
depends on SPI_MASTER && EXPERIMENTAL
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index f3226b1d38fc..93a8fefbefdf 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
+obj-$(CONFIG_MTD_MXC_DATAFLASH) += mxc_dataflash.o
diff --git a/drivers/mtd/devices/mxc_dataflash.c b/drivers/mtd/devices/mxc_dataflash.c
new file mode 100644
index 000000000000..0ed701d6778c
--- /dev/null
+++ b/drivers/mtd/devices/mxc_dataflash.c
@@ -0,0 +1,1037 @@
+/*
+ * Copyright 2009-2010 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/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <asm/mach/flash.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;
+ uint32_t rem;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%llx len 0x%llx\n",
+ dev_name(&spi->dev), (long long)instr->addr,
+ (long long)instr->len);
+
+ /* Sanity checks */
+ if (instr->addr + instr->len > mtd->size)
+ return -EINVAL;
+ div_u64_rem(instr->len, priv->page_size, &rem);
+ if (rem)
+ return -EINVAL;
+ div_u64_rem(instr->addr, priv->page_size, &rem);
+ if (rem)
+ 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 = div_u64(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 (%llx 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 f22bc9f05ddb..5362bc4a000d 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -551,4 +551,14 @@ config MTD_PISMO
When built as a module, it will be called pismo.ko
+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 bb035cd54c72..556db2ec6253 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -59,3 +59,4 @@ 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_GPIO_ADDR) += gpio-addr-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..d969f95a67a3
--- /dev/null
+++ b/drivers/mtd/maps/mxc_nor.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2004-2010 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/slab.h>
+#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 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;
+
+ 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/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 03e19c1965cc..39f48524e986 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -485,13 +485,16 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
mutex_lock(&mtd_table_mutex);
ret = register_blkdev(tr->major, tr->name);
- if (ret) {
+ if (ret < 0) {
printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
tr->name, tr->major, ret);
mutex_unlock(&mtd_table_mutex);
return ret;
}
+ if (ret)
+ tr->major = ret;
+
tr->blkshift = ffs(tr->blksize) - 1;
INIT_LIST_HEAD(&tr->devs);
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 91c8013cf0d9..8bebfa55cd4a 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -304,6 +304,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
case MTD_MODE_RAW:
{
struct mtd_oob_ops ops;
+ memset(&ops, 0, sizeof(struct mtd_oob_ops));
ops.mode = MTD_OOB_RAW;
ops.datbuf = kbuf;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index ffc3720929f1..d6849c45e3b0 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -432,6 +432,68 @@ 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 && (ARCH_MX2 || ARCH_MX3 || ARCH_MX5)
+ 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_NFC
+ tristate "GPMI NAND Flash Controller driver"
+ depends on MTD_NAND && (ARCH_MX23 || ARCH_MX28 || ARCH_MX50)
+ help
+ Enables NAND Flash support.
+
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 e8ab884ba47b..87ab9180433c 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -38,7 +38,11 @@ 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 nand_device_info.o
+obj-$(CONFIG_MTD_NAND_MXC_V3) += mxc_nd2.o nand_device_info.o
+obj-$(CONFIG_MTD_NAND_GPMI_NFC) += gpmi-nfc/ nand_device_info.o
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
diff --git a/drivers/mtd/nand/gpmi-nfc/Makefile b/drivers/mtd/nand/gpmi-nfc/Makefile
new file mode 100644
index 000000000000..5ce3d86bb921
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_MTD_NAND_GPMI_NFC) += gpmi-nfc.o
+gpmi-nfc-objs += gpmi-nfc-main.o
+gpmi-nfc-objs += gpmi-nfc-hal-common.o
+gpmi-nfc-objs += gpmi-nfc-hal-v0.o
+gpmi-nfc-objs += gpmi-nfc-hal-v1.o
+gpmi-nfc-objs += gpmi-nfc-hal-v2.o
+gpmi-nfc-objs += gpmi-nfc-rom-common.o
+gpmi-nfc-objs += gpmi-nfc-rom-v0.o
+gpmi-nfc-objs += gpmi-nfc-rom-v1.o
+gpmi-nfc-objs += gpmi-nfc-mil.o
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v0.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v0.h
new file mode 100644
index 000000000000..9af4feb29021
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v0.h
@@ -0,0 +1,550 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GPMI_NFC_BCH_REGS_H
+#define __GPMI_NFC_BCH_REGS_H
+
+/*============================================================================*/
+
+#define HW_BCH_CTRL (0x00000000)
+#define HW_BCH_CTRL_SET (0x00000004)
+#define HW_BCH_CTRL_CLR (0x00000008)
+#define HW_BCH_CTRL_TOG (0x0000000c)
+
+#define BM_BCH_CTRL_SFTRST 0x80000000
+#define BV_BCH_CTRL_SFTRST__RUN 0x0
+#define BV_BCH_CTRL_SFTRST__RESET 0x1
+#define BM_BCH_CTRL_CLKGATE 0x40000000
+#define BV_BCH_CTRL_CLKGATE__RUN 0x0
+#define BV_BCH_CTRL_CLKGATE__NO_CLKS 0x1
+#define BP_BCH_CTRL_RSVD5 23
+#define BM_BCH_CTRL_RSVD5 0x3F800000
+#define BF_BCH_CTRL_RSVD5(v) (((v) << 23) & BM_BCH_CTRL_RSVD5)
+#define BM_BCH_CTRL_DEBUGSYNDROME 0x00400000
+#define BP_BCH_CTRL_RSVD4 20
+#define BM_BCH_CTRL_RSVD4 0x00300000
+#define BF_BCH_CTRL_RSVD4(v) (((v) << 20) & BM_BCH_CTRL_RSVD4)
+#define BP_BCH_CTRL_M2M_LAYOUT 18
+#define BM_BCH_CTRL_M2M_LAYOUT 0x000C0000
+#define BF_BCH_CTRL_M2M_LAYOUT(v) (((v) << 18) & BM_BCH_CTRL_M2M_LAYOUT)
+#define BM_BCH_CTRL_M2M_ENCODE 0x00020000
+#define BM_BCH_CTRL_M2M_ENABLE 0x00010000
+#define BP_BCH_CTRL_RSVD3 11
+#define BM_BCH_CTRL_RSVD3 0x0000F800
+#define BF_BCH_CTRL_RSVD3(v) (((v) << 11) & BM_BCH_CTRL_RSVD3)
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ_EN 0x00000400
+#define BM_BCH_CTRL_RSVD2 0x00000200
+#define BM_BCH_CTRL_COMPLETE_IRQ_EN 0x00000100
+#define BP_BCH_CTRL_RSVD1 4
+#define BM_BCH_CTRL_RSVD1 0x000000F0
+#define BF_BCH_CTRL_RSVD1(v) (((v) << 4) & BM_BCH_CTRL_RSVD1)
+#define BM_BCH_CTRL_BM_ERROR_IRQ 0x00000008
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ 0x00000004
+#define BM_BCH_CTRL_RSVD0 0x00000002
+#define BM_BCH_CTRL_COMPLETE_IRQ 0x00000001
+
+/*============================================================================*/
+
+#define HW_BCH_STATUS0 (0x00000010)
+
+#define BP_BCH_STATUS0_HANDLE 20
+#define BM_BCH_STATUS0_HANDLE 0xFFF00000
+#define BF_BCH_STATUS0_HANDLE(v) \
+ (((v) << 20) & BM_BCH_STATUS0_HANDLE)
+#define BP_BCH_STATUS0_COMPLETED_CE 16
+#define BM_BCH_STATUS0_COMPLETED_CE 0x000F0000
+#define BF_BCH_STATUS0_COMPLETED_CE(v) \
+ (((v) << 16) & BM_BCH_STATUS0_COMPLETED_CE)
+#define BP_BCH_STATUS0_STATUS_BLK0 8
+#define BM_BCH_STATUS0_STATUS_BLK0 0x0000FF00
+#define BF_BCH_STATUS0_STATUS_BLK0(v) \
+ (((v) << 8) & BM_BCH_STATUS0_STATUS_BLK0)
+#define BV_BCH_STATUS0_STATUS_BLK0__ZERO 0x00
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR1 0x01
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR2 0x02
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR3 0x03
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR4 0x04
+#define BV_BCH_STATUS0_STATUS_BLK0__UNCORRECTABLE 0xFE
+#define BV_BCH_STATUS0_STATUS_BLK0__ERASED 0xFF
+#define BP_BCH_STATUS0_RSVD1 5
+#define BM_BCH_STATUS0_RSVD1 0x000000E0
+#define BF_BCH_STATUS0_RSVD1(v) \
+ (((v) << 5) & BM_BCH_STATUS0_RSVD1)
+#define BM_BCH_STATUS0_ALLONES 0x00000010
+#define BM_BCH_STATUS0_CORRECTED 0x00000008
+#define BM_BCH_STATUS0_UNCORRECTABLE 0x00000004
+#define BP_BCH_STATUS0_RSVD0 0
+#define BM_BCH_STATUS0_RSVD0 0x00000003
+#define BF_BCH_STATUS0_RSVD0(v) \
+ (((v) << 0) & BM_BCH_STATUS0_RSVD0)
+
+/*============================================================================*/
+
+#define HW_BCH_MODE (0x00000020)
+
+#define BP_BCH_MODE_RSVD 8
+#define BM_BCH_MODE_RSVD 0xFFFFFF00
+#define BF_BCH_MODE_RSVD(v) \
+ (((v) << 8) & BM_BCH_MODE_RSVD)
+#define BP_BCH_MODE_ERASE_THRESHOLD 0
+#define BM_BCH_MODE_ERASE_THRESHOLD 0x000000FF
+#define BF_BCH_MODE_ERASE_THRESHOLD(v) \
+ (((v) << 0) & BM_BCH_MODE_ERASE_THRESHOLD)
+
+/*============================================================================*/
+
+#define HW_BCH_ENCODEPTR (0x00000030)
+
+#define BP_BCH_ENCODEPTR_ADDR 0
+#define BM_BCH_ENCODEPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_ENCODEPTR_ADDR(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_DATAPTR (0x00000040)
+
+#define BP_BCH_DATAPTR_ADDR 0
+#define BM_BCH_DATAPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_DATAPTR_ADDR(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_METAPTR (0x00000050)
+
+#define BP_BCH_METAPTR_ADDR 0
+#define BM_BCH_METAPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_METAPTR_ADDR(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_LAYOUTSELECT (0x00000070)
+
+#define BP_BCH_LAYOUTSELECT_CS15_SELECT 30
+#define BM_BCH_LAYOUTSELECT_CS15_SELECT 0xC0000000
+#define BF_BCH_LAYOUTSELECT_CS15_SELECT(v) \
+ (((v) << 30) & BM_BCH_LAYOUTSELECT_CS15_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS14_SELECT 28
+#define BM_BCH_LAYOUTSELECT_CS14_SELECT 0x30000000
+#define BF_BCH_LAYOUTSELECT_CS14_SELECT(v) \
+ (((v) << 28) & BM_BCH_LAYOUTSELECT_CS14_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS13_SELECT 26
+#define BM_BCH_LAYOUTSELECT_CS13_SELECT 0x0C000000
+#define BF_BCH_LAYOUTSELECT_CS13_SELECT(v) \
+ (((v) << 26) & BM_BCH_LAYOUTSELECT_CS13_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS12_SELECT 24
+#define BM_BCH_LAYOUTSELECT_CS12_SELECT 0x03000000
+#define BF_BCH_LAYOUTSELECT_CS12_SELECT(v) \
+ (((v) << 24) & BM_BCH_LAYOUTSELECT_CS12_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS11_SELECT 22
+#define BM_BCH_LAYOUTSELECT_CS11_SELECT 0x00C00000
+#define BF_BCH_LAYOUTSELECT_CS11_SELECT(v) \
+ (((v) << 22) & BM_BCH_LAYOUTSELECT_CS11_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS10_SELECT 20
+#define BM_BCH_LAYOUTSELECT_CS10_SELECT 0x00300000
+#define BF_BCH_LAYOUTSELECT_CS10_SELECT(v) \
+ (((v) << 20) & BM_BCH_LAYOUTSELECT_CS10_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS9_SELECT 18
+#define BM_BCH_LAYOUTSELECT_CS9_SELECT 0x000C0000
+#define BF_BCH_LAYOUTSELECT_CS9_SELECT(v) \
+ (((v) << 18) & BM_BCH_LAYOUTSELECT_CS9_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS8_SELECT 16
+#define BM_BCH_LAYOUTSELECT_CS8_SELECT 0x00030000
+#define BF_BCH_LAYOUTSELECT_CS8_SELECT(v) \
+ (((v) << 16) & BM_BCH_LAYOUTSELECT_CS8_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS7_SELECT 14
+#define BM_BCH_LAYOUTSELECT_CS7_SELECT 0x0000C000
+#define BF_BCH_LAYOUTSELECT_CS7_SELECT(v) \
+ (((v) << 14) & BM_BCH_LAYOUTSELECT_CS7_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS6_SELECT 12
+#define BM_BCH_LAYOUTSELECT_CS6_SELECT 0x00003000
+#define BF_BCH_LAYOUTSELECT_CS6_SELECT(v) \
+ (((v) << 12) & BM_BCH_LAYOUTSELECT_CS6_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS5_SELECT 10
+#define BM_BCH_LAYOUTSELECT_CS5_SELECT 0x00000C00
+#define BF_BCH_LAYOUTSELECT_CS5_SELECT(v) \
+ (((v) << 10) & BM_BCH_LAYOUTSELECT_CS5_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS4_SELECT 8
+#define BM_BCH_LAYOUTSELECT_CS4_SELECT 0x00000300
+#define BF_BCH_LAYOUTSELECT_CS4_SELECT(v) \
+ (((v) << 8) & BM_BCH_LAYOUTSELECT_CS4_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS3_SELECT 6
+#define BM_BCH_LAYOUTSELECT_CS3_SELECT 0x000000C0
+#define BF_BCH_LAYOUTSELECT_CS3_SELECT(v) \
+ (((v) << 6) & BM_BCH_LAYOUTSELECT_CS3_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS2_SELECT 4
+#define BM_BCH_LAYOUTSELECT_CS2_SELECT 0x00000030
+#define BF_BCH_LAYOUTSELECT_CS2_SELECT(v) \
+ (((v) << 4) & BM_BCH_LAYOUTSELECT_CS2_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS1_SELECT 2
+#define BM_BCH_LAYOUTSELECT_CS1_SELECT 0x0000000C
+#define BF_BCH_LAYOUTSELECT_CS1_SELECT(v) \
+ (((v) << 2) & BM_BCH_LAYOUTSELECT_CS1_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS0_SELECT 0
+#define BM_BCH_LAYOUTSELECT_CS0_SELECT 0x00000003
+#define BF_BCH_LAYOUTSELECT_CS0_SELECT(v) \
+ (((v) << 0) & BM_BCH_LAYOUTSELECT_CS0_SELECT)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH0LAYOUT0 (0x00000080)
+
+#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH0LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH0LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH0LAYOUT0_ECC0 12
+#define BM_BCH_FLASH0LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH0LAYOUT0_ECC0)
+#define BV_BCH_FLASH0LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH0LAYOUT1 (0x00000090)
+
+#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH0LAYOUT1_ECCN 12
+#define BM_BCH_FLASH0LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH0LAYOUT1_ECCN)
+#define BV_BCH_FLASH0LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH1LAYOUT0 (0x000000a0)
+
+#define BP_BCH_FLASH1LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH1LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH1LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH1LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH1LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH1LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH1LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH1LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH1LAYOUT0_ECC0 12
+#define BM_BCH_FLASH1LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH1LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH1LAYOUT0_ECC0)
+#define BV_BCH_FLASH1LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH1LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH1LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH1LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH1LAYOUT0_DATA0_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH1LAYOUT1 (0x000000b0)
+
+#define BP_BCH_FLASH1LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH1LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH1LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH1LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH1LAYOUT1_ECCN 12
+#define BM_BCH_FLASH1LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH1LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH1LAYOUT1_ECCN)
+#define BV_BCH_FLASH1LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH1LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH1LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH1LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH1LAYOUT1_DATAN_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH2LAYOUT0 (0x000000c0)
+
+#define BP_BCH_FLASH2LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH2LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH2LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH2LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH2LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH2LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH2LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH2LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH2LAYOUT0_ECC0 12
+#define BM_BCH_FLASH2LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH2LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH2LAYOUT0_ECC0)
+#define BV_BCH_FLASH2LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH2LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH2LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH2LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH2LAYOUT0_DATA0_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH2LAYOUT1 (0x000000d0)
+
+#define BP_BCH_FLASH2LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH2LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH2LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH2LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH2LAYOUT1_ECCN 12
+#define BM_BCH_FLASH2LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH2LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH2LAYOUT1_ECCN)
+#define BV_BCH_FLASH2LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH2LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH2LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH2LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH2LAYOUT1_DATAN_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH3LAYOUT0 (0x000000e0)
+
+#define BP_BCH_FLASH3LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH3LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH3LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH3LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH3LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH3LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH3LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH3LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH3LAYOUT0_ECC0 12
+#define BM_BCH_FLASH3LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH3LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH3LAYOUT0_ECC0)
+#define BV_BCH_FLASH3LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH3LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH3LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH3LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH3LAYOUT0_DATA0_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH3LAYOUT1 (0x000000f0)
+
+#define BP_BCH_FLASH3LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH3LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH3LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH3LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH3LAYOUT1_ECCN 12
+#define BM_BCH_FLASH3LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH3LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH3LAYOUT1_ECCN)
+#define BV_BCH_FLASH3LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH3LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH3LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH3LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH3LAYOUT1_DATAN_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_DEBUG0 (0x00000100)
+#define HW_BCH_DEBUG0_SET (0x00000104)
+#define HW_BCH_DEBUG0_CLR (0x00000108)
+#define HW_BCH_DEBUG0_TOG (0x0000010c)
+
+#define BP_BCH_DEBUG0_RSVD1 27
+#define BM_BCH_DEBUG0_RSVD1 0xF8000000
+#define BF_BCH_DEBUG0_RSVD1(v) \
+ (((v) << 27) & BM_BCH_DEBUG0_RSVD1)
+#define BM_BCH_DEBUG0_ROM_BIST_ENABLE 0x04000000
+#define BM_BCH_DEBUG0_ROM_BIST_COMPLETE 0x02000000
+#define BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 16
+#define BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 0x01FF0000
+#define BF_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL(v) \
+ (((v) << 16) & BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL)
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_SHIFT_SYND 0x00008000
+#define BM_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG 0x00004000
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__DATA 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__AUX 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_MODE4K 0x00002000
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__4k 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__2k 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_KICK 0x00001000
+#define BM_BCH_DEBUG0_KES_STANDALONE 0x00000800
+#define BV_BCH_DEBUG0_KES_STANDALONE__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_STANDALONE__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_STEP 0x00000400
+#define BM_BCH_DEBUG0_KES_DEBUG_STALL 0x00000200
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__WAIT 0x1
+#define BM_BCH_DEBUG0_BM_KES_TEST_BYPASS 0x00000100
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__NORMAL 0x0
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__TEST_MODE 0x1
+#define BP_BCH_DEBUG0_RSVD0 6
+#define BM_BCH_DEBUG0_RSVD0 0x000000C0
+#define BF_BCH_DEBUG0_RSVD0(v) \
+ (((v) << 6) & BM_BCH_DEBUG0_RSVD0)
+#define BP_BCH_DEBUG0_DEBUG_REG_SELECT 0
+#define BM_BCH_DEBUG0_DEBUG_REG_SELECT 0x0000003F
+#define BF_BCH_DEBUG0_DEBUG_REG_SELECT(v) \
+ (((v) << 0) & BM_BCH_DEBUG0_DEBUG_REG_SELECT)
+
+/*============================================================================*/
+
+#define HW_BCH_DBGKESREAD (0x00000110)
+
+#define BP_BCH_DBGKESREAD_VALUES 0
+#define BM_BCH_DBGKESREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGKESREAD_VALUES(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_DBGCSFEREAD (0x00000120)
+
+#define BP_BCH_DBGCSFEREAD_VALUES 0
+#define BM_BCH_DBGCSFEREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGCSFEREAD_VALUES(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_DBGSYNDGENREAD (0x00000130)
+
+#define BP_BCH_DBGSYNDGENREAD_VALUES 0
+#define BM_BCH_DBGSYNDGENREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGSYNDGENREAD_VALUES(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_DBGAHBMREAD (0x00000140)
+
+#define BP_BCH_DBGAHBMREAD_VALUES 0
+#define BM_BCH_DBGAHBMREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGAHBMREAD_VALUES(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_BLOCKNAME (0x00000150)
+
+#define BP_BCH_BLOCKNAME_NAME 0
+#define BM_BCH_BLOCKNAME_NAME 0xFFFFFFFF
+#define BF_BCH_BLOCKNAME_NAME(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_VERSION (0x00000160)
+
+#define BP_BCH_VERSION_MAJOR 24
+#define BM_BCH_VERSION_MAJOR 0xFF000000
+#define BF_BCH_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_BCH_VERSION_MAJOR)
+#define BP_BCH_VERSION_MINOR 16
+#define BM_BCH_VERSION_MINOR 0x00FF0000
+#define BF_BCH_VERSION_MINOR(v) \
+ (((v) << 16) & BM_BCH_VERSION_MINOR)
+#define BP_BCH_VERSION_STEP 0
+#define BM_BCH_VERSION_STEP 0x0000FFFF
+#define BF_BCH_VERSION_STEP(v) \
+ (((v) << 0) & BM_BCH_VERSION_STEP)
+
+/*============================================================================*/
+
+#endif
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v1.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v1.h
new file mode 100644
index 000000000000..692db086de4d
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v1.h
@@ -0,0 +1,557 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Xml Revision: 2.5
+ * Template revision: 26195
+ */
+
+#ifndef __GPMI_NFC_BCH_REGS_H
+#define __GPMI_NFC_BCH_REGS_H
+
+/*============================================================================*/
+
+#define HW_BCH_CTRL (0x00000000)
+#define HW_BCH_CTRL_SET (0x00000004)
+#define HW_BCH_CTRL_CLR (0x00000008)
+#define HW_BCH_CTRL_TOG (0x0000000c)
+
+#define BM_BCH_CTRL_SFTRST 0x80000000
+#define BV_BCH_CTRL_SFTRST__RUN 0x0
+#define BV_BCH_CTRL_SFTRST__RESET 0x1
+#define BM_BCH_CTRL_CLKGATE 0x40000000
+#define BV_BCH_CTRL_CLKGATE__RUN 0x0
+#define BV_BCH_CTRL_CLKGATE__NO_CLKS 0x1
+#define BP_BCH_CTRL_RSVD5 23
+#define BM_BCH_CTRL_RSVD5 0x3F800000
+#define BF_BCH_CTRL_RSVD5(v) \
+ (((v) << 23) & BM_BCH_CTRL_RSVD5)
+#define BM_BCH_CTRL_DEBUGSYNDROME 0x00400000
+#define BP_BCH_CTRL_RSVD4 20
+#define BM_BCH_CTRL_RSVD4 0x00300000
+#define BF_BCH_CTRL_RSVD4(v) \
+ (((v) << 20) & BM_BCH_CTRL_RSVD4)
+#define BP_BCH_CTRL_M2M_LAYOUT 18
+#define BM_BCH_CTRL_M2M_LAYOUT 0x000C0000
+#define BF_BCH_CTRL_M2M_LAYOUT(v) \
+ (((v) << 18) & BM_BCH_CTRL_M2M_LAYOUT)
+#define BM_BCH_CTRL_M2M_ENCODE 0x00020000
+#define BM_BCH_CTRL_M2M_ENABLE 0x00010000
+#define BP_BCH_CTRL_RSVD3 11
+#define BM_BCH_CTRL_RSVD3 0x0000F800
+#define BF_BCH_CTRL_RSVD3(v) \
+ (((v) << 11) & BM_BCH_CTRL_RSVD3)
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ_EN 0x00000400
+#define BM_BCH_CTRL_RSVD2 0x00000200
+#define BM_BCH_CTRL_COMPLETE_IRQ_EN 0x00000100
+#define BP_BCH_CTRL_RSVD1 4
+#define BM_BCH_CTRL_RSVD1 0x000000F0
+#define BF_BCH_CTRL_RSVD1(v) \
+ (((v) << 4) & BM_BCH_CTRL_RSVD1)
+#define BM_BCH_CTRL_BM_ERROR_IRQ 0x00000008
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ 0x00000004
+#define BM_BCH_CTRL_RSVD0 0x00000002
+#define BM_BCH_CTRL_COMPLETE_IRQ 0x00000001
+
+/*============================================================================*/
+
+#define HW_BCH_STATUS0 (0x00000010)
+
+#define BP_BCH_STATUS0_HANDLE 20
+#define BM_BCH_STATUS0_HANDLE 0xFFF00000
+#define BF_BCH_STATUS0_HANDLE(v) \
+ (((v) << 20) & BM_BCH_STATUS0_HANDLE)
+#define BP_BCH_STATUS0_COMPLETED_CE 16
+#define BM_BCH_STATUS0_COMPLETED_CE 0x000F0000
+#define BF_BCH_STATUS0_COMPLETED_CE(v) \
+ (((v) << 16) & BM_BCH_STATUS0_COMPLETED_CE)
+#define BP_BCH_STATUS0_STATUS_BLK0 8
+#define BM_BCH_STATUS0_STATUS_BLK0 0x0000FF00
+#define BF_BCH_STATUS0_STATUS_BLK0(v) \
+ (((v) << 8) & BM_BCH_STATUS0_STATUS_BLK0)
+#define BV_BCH_STATUS0_STATUS_BLK0__ZERO 0x00
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR1 0x01
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR2 0x02
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR3 0x03
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR4 0x04
+#define BV_BCH_STATUS0_STATUS_BLK0__UNCORRECTABLE 0xFE
+#define BV_BCH_STATUS0_STATUS_BLK0__ERASED 0xFF
+#define BP_BCH_STATUS0_RSVD1 5
+#define BM_BCH_STATUS0_RSVD1 0x000000E0
+#define BF_BCH_STATUS0_RSVD1(v) \
+ (((v) << 5) & BM_BCH_STATUS0_RSVD1)
+#define BM_BCH_STATUS0_ALLONES 0x00000010
+#define BM_BCH_STATUS0_CORRECTED 0x00000008
+#define BM_BCH_STATUS0_UNCORRECTABLE 0x00000004
+#define BP_BCH_STATUS0_RSVD0 0
+#define BM_BCH_STATUS0_RSVD0 0x00000003
+#define BF_BCH_STATUS0_RSVD0(v) \
+ (((v) << 0) & BM_BCH_STATUS0_RSVD0)
+
+/*============================================================================*/
+
+#define HW_BCH_MODE (0x00000020)
+
+#define BP_BCH_MODE_RSVD 8
+#define BM_BCH_MODE_RSVD 0xFFFFFF00
+#define BF_BCH_MODE_RSVD(v) \
+ (((v) << 8) & BM_BCH_MODE_RSVD)
+#define BP_BCH_MODE_ERASE_THRESHOLD 0
+#define BM_BCH_MODE_ERASE_THRESHOLD 0x000000FF
+#define BF_BCH_MODE_ERASE_THRESHOLD(v) \
+ (((v) << 0) & BM_BCH_MODE_ERASE_THRESHOLD)
+
+/*============================================================================*/
+
+#define HW_BCH_ENCODEPTR (0x00000030)
+
+#define BP_BCH_ENCODEPTR_ADDR 0
+#define BM_BCH_ENCODEPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_ENCODEPTR_ADDR(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_DATAPTR (0x00000040)
+
+#define BP_BCH_DATAPTR_ADDR 0
+#define BM_BCH_DATAPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_DATAPTR_ADDR(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_METAPTR (0x00000050)
+
+#define BP_BCH_METAPTR_ADDR 0
+#define BM_BCH_METAPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_METAPTR_ADDR(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_LAYOUTSELECT (0x00000070)
+
+#define BP_BCH_LAYOUTSELECT_CS15_SELECT 30
+#define BM_BCH_LAYOUTSELECT_CS15_SELECT 0xC0000000
+#define BF_BCH_LAYOUTSELECT_CS15_SELECT(v) \
+ (((v) << 30) & BM_BCH_LAYOUTSELECT_CS15_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS14_SELECT 28
+#define BM_BCH_LAYOUTSELECT_CS14_SELECT 0x30000000
+#define BF_BCH_LAYOUTSELECT_CS14_SELECT(v) \
+ (((v) << 28) & BM_BCH_LAYOUTSELECT_CS14_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS13_SELECT 26
+#define BM_BCH_LAYOUTSELECT_CS13_SELECT 0x0C000000
+#define BF_BCH_LAYOUTSELECT_CS13_SELECT(v) \
+ (((v) << 26) & BM_BCH_LAYOUTSELECT_CS13_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS12_SELECT 24
+#define BM_BCH_LAYOUTSELECT_CS12_SELECT 0x03000000
+#define BF_BCH_LAYOUTSELECT_CS12_SELECT(v) \
+ (((v) << 24) & BM_BCH_LAYOUTSELECT_CS12_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS11_SELECT 22
+#define BM_BCH_LAYOUTSELECT_CS11_SELECT 0x00C00000
+#define BF_BCH_LAYOUTSELECT_CS11_SELECT(v) \
+ (((v) << 22) & BM_BCH_LAYOUTSELECT_CS11_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS10_SELECT 20
+#define BM_BCH_LAYOUTSELECT_CS10_SELECT 0x00300000
+#define BF_BCH_LAYOUTSELECT_CS10_SELECT(v) \
+ (((v) << 20) & BM_BCH_LAYOUTSELECT_CS10_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS9_SELECT 18
+#define BM_BCH_LAYOUTSELECT_CS9_SELECT 0x000C0000
+#define BF_BCH_LAYOUTSELECT_CS9_SELECT(v) \
+ (((v) << 18) & BM_BCH_LAYOUTSELECT_CS9_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS8_SELECT 16
+#define BM_BCH_LAYOUTSELECT_CS8_SELECT 0x00030000
+#define BF_BCH_LAYOUTSELECT_CS8_SELECT(v) \
+ (((v) << 16) & BM_BCH_LAYOUTSELECT_CS8_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS7_SELECT 14
+#define BM_BCH_LAYOUTSELECT_CS7_SELECT 0x0000C000
+#define BF_BCH_LAYOUTSELECT_CS7_SELECT(v) \
+ (((v) << 14) & BM_BCH_LAYOUTSELECT_CS7_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS6_SELECT 12
+#define BM_BCH_LAYOUTSELECT_CS6_SELECT 0x00003000
+#define BF_BCH_LAYOUTSELECT_CS6_SELECT(v) \
+ (((v) << 12) & BM_BCH_LAYOUTSELECT_CS6_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS5_SELECT 10
+#define BM_BCH_LAYOUTSELECT_CS5_SELECT 0x00000C00
+#define BF_BCH_LAYOUTSELECT_CS5_SELECT(v) \
+ (((v) << 10) & BM_BCH_LAYOUTSELECT_CS5_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS4_SELECT 8
+#define BM_BCH_LAYOUTSELECT_CS4_SELECT 0x00000300
+#define BF_BCH_LAYOUTSELECT_CS4_SELECT(v) \
+ (((v) << 8) & BM_BCH_LAYOUTSELECT_CS4_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS3_SELECT 6
+#define BM_BCH_LAYOUTSELECT_CS3_SELECT 0x000000C0
+#define BF_BCH_LAYOUTSELECT_CS3_SELECT(v) \
+ (((v) << 6) & BM_BCH_LAYOUTSELECT_CS3_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS2_SELECT 4
+#define BM_BCH_LAYOUTSELECT_CS2_SELECT 0x00000030
+#define BF_BCH_LAYOUTSELECT_CS2_SELECT(v) \
+ (((v) << 4) & BM_BCH_LAYOUTSELECT_CS2_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS1_SELECT 2
+#define BM_BCH_LAYOUTSELECT_CS1_SELECT 0x0000000C
+#define BF_BCH_LAYOUTSELECT_CS1_SELECT(v) \
+ (((v) << 2) & BM_BCH_LAYOUTSELECT_CS1_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS0_SELECT 0
+#define BM_BCH_LAYOUTSELECT_CS0_SELECT 0x00000003
+#define BF_BCH_LAYOUTSELECT_CS0_SELECT(v) \
+ (((v) << 0) & BM_BCH_LAYOUTSELECT_CS0_SELECT)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH0LAYOUT0 (0x00000080)
+
+#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH0LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH0LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH0LAYOUT0_ECC0 12
+#define BM_BCH_FLASH0LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH0LAYOUT0_ECC0)
+#define BV_BCH_FLASH0LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH0LAYOUT1 (0x00000090)
+
+#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH0LAYOUT1_ECCN 12
+#define BM_BCH_FLASH0LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH0LAYOUT1_ECCN)
+#define BV_BCH_FLASH0LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH1LAYOUT0 (0x000000a0)
+
+#define BP_BCH_FLASH1LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH1LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH1LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH1LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH1LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH1LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH1LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH1LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH1LAYOUT0_ECC0 12
+#define BM_BCH_FLASH1LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH1LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH1LAYOUT0_ECC0)
+#define BV_BCH_FLASH1LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH1LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH1LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH1LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH1LAYOUT0_DATA0_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH1LAYOUT1 (0x000000b0)
+
+#define BP_BCH_FLASH1LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH1LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH1LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH1LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH1LAYOUT1_ECCN 12
+#define BM_BCH_FLASH1LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH1LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH1LAYOUT1_ECCN)
+#define BV_BCH_FLASH1LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH1LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH1LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH1LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH1LAYOUT1_DATAN_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH2LAYOUT0 (0x000000c0)
+
+#define BP_BCH_FLASH2LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH2LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH2LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH2LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH2LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH2LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH2LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH2LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH2LAYOUT0_ECC0 12
+#define BM_BCH_FLASH2LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH2LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH2LAYOUT0_ECC0)
+#define BV_BCH_FLASH2LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH2LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH2LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH2LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH2LAYOUT0_DATA0_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH2LAYOUT1 (0x000000d0)
+
+#define BP_BCH_FLASH2LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH2LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH2LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH2LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH2LAYOUT1_ECCN 12
+#define BM_BCH_FLASH2LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH2LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH2LAYOUT1_ECCN)
+#define BV_BCH_FLASH2LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH2LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH2LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH2LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH2LAYOUT1_DATAN_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH3LAYOUT0 (0x000000e0)
+
+#define BP_BCH_FLASH3LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH3LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH3LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH3LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH3LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH3LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH3LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH3LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH3LAYOUT0_ECC0 12
+#define BM_BCH_FLASH3LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH3LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH3LAYOUT0_ECC0)
+#define BV_BCH_FLASH3LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH3LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH3LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH3LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH3LAYOUT0_DATA0_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_FLASH3LAYOUT1 (0x000000f0)
+
+#define BP_BCH_FLASH3LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH3LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH3LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH3LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH3LAYOUT1_ECCN 12
+#define BM_BCH_FLASH3LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH3LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH3LAYOUT1_ECCN)
+#define BV_BCH_FLASH3LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH3LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH3LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH3LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH3LAYOUT1_DATAN_SIZE)
+
+/*============================================================================*/
+
+#define HW_BCH_DEBUG0 (0x00000100)
+#define HW_BCH_DEBUG0_SET (0x00000104)
+#define HW_BCH_DEBUG0_CLR (0x00000108)
+#define HW_BCH_DEBUG0_TOG (0x0000010c)
+
+#define BP_BCH_DEBUG0_RSVD1 27
+#define BM_BCH_DEBUG0_RSVD1 0xF8000000
+#define BF_BCH_DEBUG0_RSVD1(v) \
+ (((v) << 27) & BM_BCH_DEBUG0_RSVD1)
+#define BM_BCH_DEBUG0_ROM_BIST_ENABLE 0x04000000
+#define BM_BCH_DEBUG0_ROM_BIST_COMPLETE 0x02000000
+#define BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 16
+#define BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 0x01FF0000
+#define BF_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL(v) \
+ (((v) << 16) & BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL)
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_SHIFT_SYND 0x00008000
+#define BM_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG 0x00004000
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__DATA 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__AUX 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_MODE4K 0x00002000
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__4k 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__2k 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_KICK 0x00001000
+#define BM_BCH_DEBUG0_KES_STANDALONE 0x00000800
+#define BV_BCH_DEBUG0_KES_STANDALONE__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_STANDALONE__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_STEP 0x00000400
+#define BM_BCH_DEBUG0_KES_DEBUG_STALL 0x00000200
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__WAIT 0x1
+#define BM_BCH_DEBUG0_BM_KES_TEST_BYPASS 0x00000100
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__NORMAL 0x0
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__TEST_MODE 0x1
+#define BP_BCH_DEBUG0_RSVD0 6
+#define BM_BCH_DEBUG0_RSVD0 0x000000C0
+#define BF_BCH_DEBUG0_RSVD0(v) \
+ (((v) << 6) & BM_BCH_DEBUG0_RSVD0)
+#define BP_BCH_DEBUG0_DEBUG_REG_SELECT 0
+#define BM_BCH_DEBUG0_DEBUG_REG_SELECT 0x0000003F
+#define BF_BCH_DEBUG0_DEBUG_REG_SELECT(v) \
+ (((v) << 0) & BM_BCH_DEBUG0_DEBUG_REG_SELECT)
+
+/*============================================================================*/
+
+#define HW_BCH_DBGKESREAD (0x00000110)
+
+#define BP_BCH_DBGKESREAD_VALUES 0
+#define BM_BCH_DBGKESREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGKESREAD_VALUES(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_DBGCSFEREAD (0x00000120)
+
+#define BP_BCH_DBGCSFEREAD_VALUES 0
+#define BM_BCH_DBGCSFEREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGCSFEREAD_VALUES(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_DBGSYNDGENREAD (0x00000130)
+
+#define BP_BCH_DBGSYNDGENREAD_VALUES 0
+#define BM_BCH_DBGSYNDGENREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGSYNDGENREAD_VALUES(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_DBGAHBMREAD (0x00000140)
+
+#define BP_BCH_DBGAHBMREAD_VALUES 0
+#define BM_BCH_DBGAHBMREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGAHBMREAD_VALUES(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_BLOCKNAME (0x00000150)
+
+#define BP_BCH_BLOCKNAME_NAME 0
+#define BM_BCH_BLOCKNAME_NAME 0xFFFFFFFF
+#define BF_BCH_BLOCKNAME_NAME(v) (v)
+
+/*============================================================================*/
+
+#define HW_BCH_VERSION (0x00000160)
+
+#define BP_BCH_VERSION_MAJOR 24
+#define BM_BCH_VERSION_MAJOR 0xFF000000
+#define BF_BCH_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_BCH_VERSION_MAJOR)
+#define BP_BCH_VERSION_MINOR 16
+#define BM_BCH_VERSION_MINOR 0x00FF0000
+#define BF_BCH_VERSION_MINOR(v) \
+ (((v) << 16) & BM_BCH_VERSION_MINOR)
+#define BP_BCH_VERSION_STEP 0
+#define BM_BCH_VERSION_STEP 0x0000FFFF
+#define BF_BCH_VERSION_STEP(v) \
+ (((v) << 0) & BM_BCH_VERSION_STEP)
+
+/*============================================================================*/
+
+#endif
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v2.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v2.h
new file mode 100644
index 000000000000..46c0ceb6ddff
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-bch-regs-v2.h
@@ -0,0 +1,567 @@
+/*
+ * Freescale BCH Register Definitions
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This file is created by xml file. Don't Edit it.
+ *
+ * Xml Revision: 1.3
+ * Template revision: 1.3
+ */
+
+#ifndef __ARCH_ARM___BCH_H
+#define __ARCH_ARM___BCH_H
+
+
+#define HW_BCH_CTRL (0x00000000)
+#define HW_BCH_CTRL_SET (0x00000004)
+#define HW_BCH_CTRL_CLR (0x00000008)
+#define HW_BCH_CTRL_TOG (0x0000000c)
+
+#define BM_BCH_CTRL_SFTRST 0x80000000
+#define BV_BCH_CTRL_SFTRST__RUN 0x0
+#define BV_BCH_CTRL_SFTRST__RESET 0x1
+#define BM_BCH_CTRL_CLKGATE 0x40000000
+#define BV_BCH_CTRL_CLKGATE__RUN 0x0
+#define BV_BCH_CTRL_CLKGATE__NO_CLKS 0x1
+#define BP_BCH_CTRL_RSVD5 23
+#define BM_BCH_CTRL_RSVD5 0x3F800000
+#define BF_BCH_CTRL_RSVD5(v) \
+ (((v) << 23) & BM_BCH_CTRL_RSVD5)
+#define BM_BCH_CTRL_DEBUGSYNDROME 0x00400000
+#define BP_BCH_CTRL_RSVD4 20
+#define BM_BCH_CTRL_RSVD4 0x00300000
+#define BF_BCH_CTRL_RSVD4(v) \
+ (((v) << 20) & BM_BCH_CTRL_RSVD4)
+#define BP_BCH_CTRL_M2M_LAYOUT 18
+#define BM_BCH_CTRL_M2M_LAYOUT 0x000C0000
+#define BF_BCH_CTRL_M2M_LAYOUT(v) \
+ (((v) << 18) & BM_BCH_CTRL_M2M_LAYOUT)
+#define BM_BCH_CTRL_M2M_ENCODE 0x00020000
+#define BM_BCH_CTRL_M2M_ENABLE 0x00010000
+#define BP_BCH_CTRL_RSVD3 11
+#define BM_BCH_CTRL_RSVD3 0x0000F800
+#define BF_BCH_CTRL_RSVD3(v) \
+ (((v) << 11) & BM_BCH_CTRL_RSVD3)
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ_EN 0x00000400
+#define BM_BCH_CTRL_RSVD2 0x00000200
+#define BM_BCH_CTRL_COMPLETE_IRQ_EN 0x00000100
+#define BP_BCH_CTRL_RSVD1 4
+#define BM_BCH_CTRL_RSVD1 0x000000F0
+#define BF_BCH_CTRL_RSVD1(v) \
+ (((v) << 4) & BM_BCH_CTRL_RSVD1)
+#define BM_BCH_CTRL_BM_ERROR_IRQ 0x00000008
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ 0x00000004
+#define BM_BCH_CTRL_RSVD0 0x00000002
+#define BM_BCH_CTRL_COMPLETE_IRQ 0x00000001
+
+#define HW_BCH_STATUS0 (0x00000010)
+
+#define BP_BCH_STATUS0_HANDLE 20
+#define BM_BCH_STATUS0_HANDLE 0xFFF00000
+#define BF_BCH_STATUS0_HANDLE(v) \
+ (((v) << 20) & BM_BCH_STATUS0_HANDLE)
+#define BP_BCH_STATUS0_COMPLETED_CE 16
+#define BM_BCH_STATUS0_COMPLETED_CE 0x000F0000
+#define BF_BCH_STATUS0_COMPLETED_CE(v) \
+ (((v) << 16) & BM_BCH_STATUS0_COMPLETED_CE)
+#define BP_BCH_STATUS0_STATUS_BLK0 8
+#define BM_BCH_STATUS0_STATUS_BLK0 0x0000FF00
+#define BF_BCH_STATUS0_STATUS_BLK0(v) \
+ (((v) << 8) & BM_BCH_STATUS0_STATUS_BLK0)
+#define BV_BCH_STATUS0_STATUS_BLK0__ZERO 0x00
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR1 0x01
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR2 0x02
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR3 0x03
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR4 0x04
+#define BV_BCH_STATUS0_STATUS_BLK0__UNCORRECTABLE 0xFE
+#define BV_BCH_STATUS0_STATUS_BLK0__ERASED 0xFF
+#define BP_BCH_STATUS0_RSVD1 5
+#define BM_BCH_STATUS0_RSVD1 0x000000E0
+#define BF_BCH_STATUS0_RSVD1(v) \
+ (((v) << 5) & BM_BCH_STATUS0_RSVD1)
+#define BM_BCH_STATUS0_ALLONES 0x00000010
+#define BM_BCH_STATUS0_CORRECTED 0x00000008
+#define BM_BCH_STATUS0_UNCORRECTABLE 0x00000004
+#define BP_BCH_STATUS0_RSVD0 0
+#define BM_BCH_STATUS0_RSVD0 0x00000003
+#define BF_BCH_STATUS0_RSVD0(v) \
+ (((v) << 0) & BM_BCH_STATUS0_RSVD0)
+
+#define HW_BCH_MODE (0x00000020)
+
+#define BP_BCH_MODE_RSVD 8
+#define BM_BCH_MODE_RSVD 0xFFFFFF00
+#define BF_BCH_MODE_RSVD(v) \
+ (((v) << 8) & BM_BCH_MODE_RSVD)
+#define BP_BCH_MODE_ERASE_THRESHOLD 0
+#define BM_BCH_MODE_ERASE_THRESHOLD 0x000000FF
+#define BF_BCH_MODE_ERASE_THRESHOLD(v) \
+ (((v) << 0) & BM_BCH_MODE_ERASE_THRESHOLD)
+
+#define HW_BCH_ENCODEPTR (0x00000030)
+
+#define BP_BCH_ENCODEPTR_ADDR 0
+#define BM_BCH_ENCODEPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_ENCODEPTR_ADDR(v) (v)
+
+#define HW_BCH_DATAPTR (0x00000040)
+
+#define BP_BCH_DATAPTR_ADDR 0
+#define BM_BCH_DATAPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_DATAPTR_ADDR(v) (v)
+
+#define HW_BCH_METAPTR (0x00000050)
+
+#define BP_BCH_METAPTR_ADDR 0
+#define BM_BCH_METAPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_METAPTR_ADDR(v) (v)
+
+#define HW_BCH_LAYOUTSELECT (0x00000070)
+
+#define BP_BCH_LAYOUTSELECT_CS15_SELECT 30
+#define BM_BCH_LAYOUTSELECT_CS15_SELECT 0xC0000000
+#define BF_BCH_LAYOUTSELECT_CS15_SELECT(v) \
+ (((v) << 30) & BM_BCH_LAYOUTSELECT_CS15_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS14_SELECT 28
+#define BM_BCH_LAYOUTSELECT_CS14_SELECT 0x30000000
+#define BF_BCH_LAYOUTSELECT_CS14_SELECT(v) \
+ (((v) << 28) & BM_BCH_LAYOUTSELECT_CS14_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS13_SELECT 26
+#define BM_BCH_LAYOUTSELECT_CS13_SELECT 0x0C000000
+#define BF_BCH_LAYOUTSELECT_CS13_SELECT(v) \
+ (((v) << 26) & BM_BCH_LAYOUTSELECT_CS13_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS12_SELECT 24
+#define BM_BCH_LAYOUTSELECT_CS12_SELECT 0x03000000
+#define BF_BCH_LAYOUTSELECT_CS12_SELECT(v) \
+ (((v) << 24) & BM_BCH_LAYOUTSELECT_CS12_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS11_SELECT 22
+#define BM_BCH_LAYOUTSELECT_CS11_SELECT 0x00C00000
+#define BF_BCH_LAYOUTSELECT_CS11_SELECT(v) \
+ (((v) << 22) & BM_BCH_LAYOUTSELECT_CS11_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS10_SELECT 20
+#define BM_BCH_LAYOUTSELECT_CS10_SELECT 0x00300000
+#define BF_BCH_LAYOUTSELECT_CS10_SELECT(v) \
+ (((v) << 20) & BM_BCH_LAYOUTSELECT_CS10_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS9_SELECT 18
+#define BM_BCH_LAYOUTSELECT_CS9_SELECT 0x000C0000
+#define BF_BCH_LAYOUTSELECT_CS9_SELECT(v) \
+ (((v) << 18) & BM_BCH_LAYOUTSELECT_CS9_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS8_SELECT 16
+#define BM_BCH_LAYOUTSELECT_CS8_SELECT 0x00030000
+#define BF_BCH_LAYOUTSELECT_CS8_SELECT(v) \
+ (((v) << 16) & BM_BCH_LAYOUTSELECT_CS8_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS7_SELECT 14
+#define BM_BCH_LAYOUTSELECT_CS7_SELECT 0x0000C000
+#define BF_BCH_LAYOUTSELECT_CS7_SELECT(v) \
+ (((v) << 14) & BM_BCH_LAYOUTSELECT_CS7_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS6_SELECT 12
+#define BM_BCH_LAYOUTSELECT_CS6_SELECT 0x00003000
+#define BF_BCH_LAYOUTSELECT_CS6_SELECT(v) \
+ (((v) << 12) & BM_BCH_LAYOUTSELECT_CS6_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS5_SELECT 10
+#define BM_BCH_LAYOUTSELECT_CS5_SELECT 0x00000C00
+#define BF_BCH_LAYOUTSELECT_CS5_SELECT(v) \
+ (((v) << 10) & BM_BCH_LAYOUTSELECT_CS5_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS4_SELECT 8
+#define BM_BCH_LAYOUTSELECT_CS4_SELECT 0x00000300
+#define BF_BCH_LAYOUTSELECT_CS4_SELECT(v) \
+ (((v) << 8) & BM_BCH_LAYOUTSELECT_CS4_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS3_SELECT 6
+#define BM_BCH_LAYOUTSELECT_CS3_SELECT 0x000000C0
+#define BF_BCH_LAYOUTSELECT_CS3_SELECT(v) \
+ (((v) << 6) & BM_BCH_LAYOUTSELECT_CS3_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS2_SELECT 4
+#define BM_BCH_LAYOUTSELECT_CS2_SELECT 0x00000030
+#define BF_BCH_LAYOUTSELECT_CS2_SELECT(v) \
+ (((v) << 4) & BM_BCH_LAYOUTSELECT_CS2_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS1_SELECT 2
+#define BM_BCH_LAYOUTSELECT_CS1_SELECT 0x0000000C
+#define BF_BCH_LAYOUTSELECT_CS1_SELECT(v) \
+ (((v) << 2) & BM_BCH_LAYOUTSELECT_CS1_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS0_SELECT 0
+#define BM_BCH_LAYOUTSELECT_CS0_SELECT 0x00000003
+#define BF_BCH_LAYOUTSELECT_CS0_SELECT(v) \
+ (((v) << 0) & BM_BCH_LAYOUTSELECT_CS0_SELECT)
+
+#define HW_BCH_FLASH0LAYOUT0 (0x00000080)
+
+#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH0LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH0LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH0LAYOUT0_ECC0 11
+#define BM_BCH_FLASH0LAYOUT0_ECC0 0x0000F800
+#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \
+ (((v) << 11) & BM_BCH_FLASH0LAYOUT0_ECC0)
+#define BV_BCH_FLASH0LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC20 0xA
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC22 0xB
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC24 0xC
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC26 0xD
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC28 0xE
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC30 0xF
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC32 0x10
+#define BM_BCH_FLASH0LAYOUT0_GF13_0_GF14_1 0x00000400
+#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE 0x000003FF
+#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+
+#define HW_BCH_FLASH0LAYOUT1 (0x00000090)
+
+#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH0LAYOUT1_ECCN 11
+#define BM_BCH_FLASH0LAYOUT1_ECCN 0x0000F800
+#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \
+ (((v) << 11) & BM_BCH_FLASH0LAYOUT1_ECCN)
+#define BV_BCH_FLASH0LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC20 0xA
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC22 0xB
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC24 0xC
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC26 0xD
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC28 0xE
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC30 0xF
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC32 0x10
+#define BM_BCH_FLASH0LAYOUT1_GF13_0_GF14_1 0x00000400
+#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE 0x000003FF
+#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+
+#define HW_BCH_FLASH1LAYOUT0 (0x000000a0)
+
+#define BP_BCH_FLASH1LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH1LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH1LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH1LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH1LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH1LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH1LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH1LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH1LAYOUT0_ECC0 11
+#define BM_BCH_FLASH1LAYOUT0_ECC0 0x0000F800
+#define BF_BCH_FLASH1LAYOUT0_ECC0(v) \
+ (((v) << 11) & BM_BCH_FLASH1LAYOUT0_ECC0)
+#define BV_BCH_FLASH1LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC20 0xA
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC22 0xB
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC24 0xC
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC26 0xD
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC28 0xE
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC30 0xF
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC32 0x10
+#define BM_BCH_FLASH1LAYOUT0_GF13_0_GF14_1 0x00000400
+#define BP_BCH_FLASH1LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH1LAYOUT0_DATA0_SIZE 0x000003FF
+#define BF_BCH_FLASH1LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH1LAYOUT0_DATA0_SIZE)
+
+#define HW_BCH_FLASH1LAYOUT1 (0x000000b0)
+
+#define BP_BCH_FLASH1LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH1LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH1LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH1LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH1LAYOUT1_ECCN 11
+#define BM_BCH_FLASH1LAYOUT1_ECCN 0x0000F800
+#define BF_BCH_FLASH1LAYOUT1_ECCN(v) \
+ (((v) << 11) & BM_BCH_FLASH1LAYOUT1_ECCN)
+#define BV_BCH_FLASH1LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC20 0xA
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC22 0xB
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC24 0xC
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC26 0xD
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC28 0xE
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC30 0xF
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC32 0x10
+#define BM_BCH_FLASH1LAYOUT1_GF13_0_GF14_1 0x00000400
+#define BP_BCH_FLASH1LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH1LAYOUT1_DATAN_SIZE 0x000003FF
+#define BF_BCH_FLASH1LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH1LAYOUT1_DATAN_SIZE)
+
+#define HW_BCH_FLASH2LAYOUT0 (0x000000c0)
+
+#define BP_BCH_FLASH2LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH2LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH2LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH2LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH2LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH2LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH2LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH2LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH2LAYOUT0_ECC0 11
+#define BM_BCH_FLASH2LAYOUT0_ECC0 0x0000F800
+#define BF_BCH_FLASH2LAYOUT0_ECC0(v) \
+ (((v) << 11) & BM_BCH_FLASH2LAYOUT0_ECC0)
+#define BV_BCH_FLASH2LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC20 0xA
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC22 0xB
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC24 0xC
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC26 0xD
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC28 0xE
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC30 0xF
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC32 0x10
+#define BM_BCH_FLASH2LAYOUT0_GF13_0_GF14_1 0x00000400
+#define BP_BCH_FLASH2LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH2LAYOUT0_DATA0_SIZE 0x000003FF
+#define BF_BCH_FLASH2LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH2LAYOUT0_DATA0_SIZE)
+
+#define HW_BCH_FLASH2LAYOUT1 (0x000000d0)
+
+#define BP_BCH_FLASH2LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH2LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH2LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH2LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH2LAYOUT1_ECCN 11
+#define BM_BCH_FLASH2LAYOUT1_ECCN 0x0000F800
+#define BF_BCH_FLASH2LAYOUT1_ECCN(v) \
+ (((v) << 11) & BM_BCH_FLASH2LAYOUT1_ECCN)
+#define BV_BCH_FLASH2LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC20 0xA
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC22 0xB
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC24 0xC
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC26 0xD
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC28 0xE
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC30 0xF
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC32 0x10
+#define BM_BCH_FLASH2LAYOUT1_GF13_0_GF14_1 0x00000400
+#define BP_BCH_FLASH2LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH2LAYOUT1_DATAN_SIZE 0x000003FF
+#define BF_BCH_FLASH2LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH2LAYOUT1_DATAN_SIZE)
+
+#define HW_BCH_FLASH3LAYOUT0 (0x000000e0)
+
+#define BP_BCH_FLASH3LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH3LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH3LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH3LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH3LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH3LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH3LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH3LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH3LAYOUT0_ECC0 11
+#define BM_BCH_FLASH3LAYOUT0_ECC0 0x0000F800
+#define BF_BCH_FLASH3LAYOUT0_ECC0(v) \
+ (((v) << 11) & BM_BCH_FLASH3LAYOUT0_ECC0)
+#define BV_BCH_FLASH3LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC20 0xA
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC22 0xB
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC24 0xC
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC26 0xD
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC28 0xE
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC30 0xF
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC32 0x10
+#define BM_BCH_FLASH3LAYOUT0_GF13_0_GF14_1 0x00000400
+#define BP_BCH_FLASH3LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH3LAYOUT0_DATA0_SIZE 0x000003FF
+#define BF_BCH_FLASH3LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH3LAYOUT0_DATA0_SIZE)
+
+#define HW_BCH_FLASH3LAYOUT1 (0x000000f0)
+
+#define BP_BCH_FLASH3LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH3LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH3LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH3LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH3LAYOUT1_ECCN 11
+#define BM_BCH_FLASH3LAYOUT1_ECCN 0x0000F800
+#define BF_BCH_FLASH3LAYOUT1_ECCN(v) \
+ (((v) << 11) & BM_BCH_FLASH3LAYOUT1_ECCN)
+#define BV_BCH_FLASH3LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC20 0xA
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC22 0xB
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC24 0xC
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC26 0xD
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC28 0xE
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC30 0xF
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC32 0x10
+#define BM_BCH_FLASH3LAYOUT1_GF13_0_GF14_1 0x00000400
+#define BP_BCH_FLASH3LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH3LAYOUT1_DATAN_SIZE 0x000003FF
+#define BF_BCH_FLASH3LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH3LAYOUT1_DATAN_SIZE)
+
+#define HW_BCH_DEBUG0 (0x00000100)
+#define HW_BCH_DEBUG0_SET (0x00000104)
+#define HW_BCH_DEBUG0_CLR (0x00000108)
+#define HW_BCH_DEBUG0_TOG (0x0000010c)
+
+#define BP_BCH_DEBUG0_RSVD1 25
+#define BM_BCH_DEBUG0_RSVD1 0xFE000000
+#define BF_BCH_DEBUG0_RSVD1(v) \
+ (((v) << 25) & BM_BCH_DEBUG0_RSVD1)
+#define BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 16
+#define BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 0x01FF0000
+#define BF_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL(v) \
+ (((v) << 16) & BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL)
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_SHIFT_SYND 0x00008000
+#define BM_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG 0x00004000
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__DATA 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__AUX 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_MODE4K 0x00002000
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__4k 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__2k 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_KICK 0x00001000
+#define BM_BCH_DEBUG0_KES_STANDALONE 0x00000800
+#define BV_BCH_DEBUG0_KES_STANDALONE__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_STANDALONE__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_STEP 0x00000400
+#define BM_BCH_DEBUG0_KES_DEBUG_STALL 0x00000200
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__WAIT 0x1
+#define BM_BCH_DEBUG0_BM_KES_TEST_BYPASS 0x00000100
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__NORMAL 0x0
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__TEST_MODE 0x1
+#define BP_BCH_DEBUG0_RSVD0 6
+#define BM_BCH_DEBUG0_RSVD0 0x000000C0
+#define BF_BCH_DEBUG0_RSVD0(v) \
+ (((v) << 6) & BM_BCH_DEBUG0_RSVD0)
+#define BP_BCH_DEBUG0_DEBUG_REG_SELECT 0
+#define BM_BCH_DEBUG0_DEBUG_REG_SELECT 0x0000003F
+#define BF_BCH_DEBUG0_DEBUG_REG_SELECT(v) \
+ (((v) << 0) & BM_BCH_DEBUG0_DEBUG_REG_SELECT)
+
+#define HW_BCH_DBGKESREAD (0x00000110)
+
+#define BP_BCH_DBGKESREAD_VALUES 0
+#define BM_BCH_DBGKESREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGKESREAD_VALUES(v) (v)
+
+#define HW_BCH_DBGCSFEREAD (0x00000120)
+
+#define BP_BCH_DBGCSFEREAD_VALUES 0
+#define BM_BCH_DBGCSFEREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGCSFEREAD_VALUES(v) (v)
+
+#define HW_BCH_DBGSYNDGENREAD (0x00000130)
+
+#define BP_BCH_DBGSYNDGENREAD_VALUES 0
+#define BM_BCH_DBGSYNDGENREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGSYNDGENREAD_VALUES(v) (v)
+
+#define HW_BCH_DBGAHBMREAD (0x00000140)
+
+#define BP_BCH_DBGAHBMREAD_VALUES 0
+#define BM_BCH_DBGAHBMREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGAHBMREAD_VALUES(v) (v)
+
+#define HW_BCH_BLOCKNAME (0x00000150)
+
+#define BP_BCH_BLOCKNAME_NAME 0
+#define BM_BCH_BLOCKNAME_NAME 0xFFFFFFFF
+#define BF_BCH_BLOCKNAME_NAME(v) (v)
+
+#define HW_BCH_VERSION (0x00000160)
+
+#define BP_BCH_VERSION_MAJOR 24
+#define BM_BCH_VERSION_MAJOR 0xFF000000
+#define BF_BCH_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_BCH_VERSION_MAJOR)
+#define BP_BCH_VERSION_MINOR 16
+#define BM_BCH_VERSION_MINOR 0x00FF0000
+#define BF_BCH_VERSION_MINOR(v) \
+ (((v) << 16) & BM_BCH_VERSION_MINOR)
+#define BP_BCH_VERSION_STEP 0
+#define BM_BCH_VERSION_STEP 0x0000FFFF
+#define BF_BCH_VERSION_STEP(v) \
+ (((v) << 0) & BM_BCH_VERSION_STEP)
+#endif /* __ARCH_ARM___BCH_H */
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v0.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v0.h
new file mode 100644
index 000000000000..2f9fce609a34
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v0.h
@@ -0,0 +1,416 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GPMI_NFC_GPMI_REGS_H
+#define __GPMI_NFC_GPMI_REGS_H
+
+/*============================================================================*/
+
+#define HW_GPMI_CTRL0 (0x00000000)
+#define HW_GPMI_CTRL0_SET (0x00000004)
+#define HW_GPMI_CTRL0_CLR (0x00000008)
+#define HW_GPMI_CTRL0_TOG (0x0000000c)
+
+#define BM_GPMI_CTRL0_SFTRST 0x80000000
+#define BV_GPMI_CTRL0_SFTRST__RUN 0x0
+#define BV_GPMI_CTRL0_SFTRST__RESET 0x1
+#define BM_GPMI_CTRL0_CLKGATE 0x40000000
+#define BV_GPMI_CTRL0_CLKGATE__RUN 0x0
+#define BV_GPMI_CTRL0_CLKGATE__NO_CLKS 0x1
+#define BM_GPMI_CTRL0_RUN 0x20000000
+#define BV_GPMI_CTRL0_RUN__IDLE 0x0
+#define BV_GPMI_CTRL0_RUN__BUSY 0x1
+#define BM_GPMI_CTRL0_DEV_IRQ_EN 0x10000000
+#define BM_GPMI_CTRL0_TIMEOUT_IRQ_EN 0x08000000
+#define BM_GPMI_CTRL0_UDMA 0x04000000
+#define BV_GPMI_CTRL0_UDMA__DISABLED 0x0
+#define BV_GPMI_CTRL0_UDMA__ENABLED 0x1
+#define BP_GPMI_CTRL0_COMMAND_MODE 24
+#define BM_GPMI_CTRL0_COMMAND_MODE 0x03000000
+#define BF_GPMI_CTRL0_COMMAND_MODE(v) \
+ (((v) << 24) & BM_GPMI_CTRL0_COMMAND_MODE)
+#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2
+#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3
+#define BM_GPMI_CTRL0_WORD_LENGTH 0x00800000
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1
+#define BM_GPMI_CTRL0_LOCK_CS 0x00400000
+#define BV_GPMI_CTRL0_LOCK_CS__DISABLED 0x0
+#define BV_GPMI_CTRL0_LOCK_CS__ENABLED 0x1
+#define BP_GPMI_CTRL0_CS 20
+#define BM_GPMI_CTRL0_CS 0x00300000
+#define BF_GPMI_CTRL0_CS(v) (((v) << 20) & BM_GPMI_CTRL0_CS)
+#define BP_GPMI_CTRL0_ADDRESS 17
+#define BM_GPMI_CTRL0_ADDRESS 0x000E0000
+#define BF_GPMI_CTRL0_ADDRESS(v) (((v) << 17) & BM_GPMI_CTRL0_ADDRESS)
+#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0
+#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1
+#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2
+#define BM_GPMI_CTRL0_ADDRESS_INCREMENT 0x00010000
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1
+#define BP_GPMI_CTRL0_XFER_COUNT 0
+#define BM_GPMI_CTRL0_XFER_COUNT 0x0000FFFF
+#define BF_GPMI_CTRL0_XFER_COUNT(v) \
+ (((v) << 0) & BM_GPMI_CTRL0_XFER_COUNT)
+
+/*============================================================================*/
+
+#define HW_GPMI_COMPARE (0x00000010)
+
+#define BP_GPMI_COMPARE_MASK 16
+#define BM_GPMI_COMPARE_MASK 0xFFFF0000
+#define BF_GPMI_COMPARE_MASK(v) (((v) << 16) & BM_GPMI_COMPARE_MASK)
+#define BP_GPMI_COMPARE_REFERENCE 0
+#define BM_GPMI_COMPARE_REFERENCE 0x0000FFFF
+#define BF_GPMI_COMPARE_REFERENCE(v) \
+ (((v) << 0) & BM_GPMI_COMPARE_REFERENCE)
+
+/*============================================================================*/
+
+#define HW_GPMI_ECCCTRL (0x00000020)
+#define HW_GPMI_ECCCTRL_SET (0x00000024)
+#define HW_GPMI_ECCCTRL_CLR (0x00000028)
+#define HW_GPMI_ECCCTRL_TOG (0x0000002c)
+
+#define BP_GPMI_ECCCTRL_HANDLE 16
+#define BM_GPMI_ECCCTRL_HANDLE 0xFFFF0000
+#define BF_GPMI_ECCCTRL_HANDLE(v) (((v) << 16) & BM_GPMI_ECCCTRL_HANDLE)
+#define BM_GPMI_ECCCTRL_RSVD2 0x00008000
+#define BP_GPMI_ECCCTRL_ECC_CMD 13
+#define BM_GPMI_ECCCTRL_ECC_CMD 0x00006000
+#define BF_GPMI_ECCCTRL_ECC_CMD(v) (((v) << 13) & BM_GPMI_ECCCTRL_ECC_CMD)
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_4_BIT 0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT 0x1
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT 0x2
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT 0x3
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1
+#define BM_GPMI_ECCCTRL_ENABLE_ECC 0x00001000
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0
+#define BP_GPMI_ECCCTRL_RSVD1 9
+#define BM_GPMI_ECCCTRL_RSVD1 0x00000E00
+#define BF_GPMI_ECCCTRL_RSVD1(v) (((v) << 9) & BM_GPMI_ECCCTRL_RSVD1)
+#define BP_GPMI_ECCCTRL_BUFFER_MASK 0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK 0x000001FF
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \
+ (((v) << 0) & BM_GPMI_ECCCTRL_BUFFER_MASK)
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER7 0x080
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER6 0x040
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER5 0x020
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER4 0x010
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER3 0x008
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER2 0x004
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER1 0x002
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER0 0x001
+
+/*============================================================================*/
+
+#define HW_GPMI_ECCCOUNT (0x00000030)
+
+#define BP_GPMI_ECCCOUNT_RSVD2 16
+#define BM_GPMI_ECCCOUNT_RSVD2 0xFFFF0000
+#define BF_GPMI_ECCCOUNT_RSVD2(v) (((v) << 16) & BM_GPMI_ECCCOUNT_RSVD2)
+#define BP_GPMI_ECCCOUNT_COUNT 0
+#define BM_GPMI_ECCCOUNT_COUNT 0x0000FFFF
+#define BF_GPMI_ECCCOUNT_COUNT(v) (((v) << 0) & BM_GPMI_ECCCOUNT_COUNT)
+
+/*============================================================================*/
+
+#define HW_GPMI_PAYLOAD (0x00000040)
+
+#define BP_GPMI_PAYLOAD_ADDRESS 2
+#define BM_GPMI_PAYLOAD_ADDRESS 0xFFFFFFFC
+#define BF_GPMI_PAYLOAD_ADDRESS(v) (((v) << 2) & BM_GPMI_PAYLOAD_ADDRESS)
+#define BP_GPMI_PAYLOAD_RSVD0 0
+#define BM_GPMI_PAYLOAD_RSVD0 0x00000003
+#define BF_GPMI_PAYLOAD_RSVD0(v) (((v) << 0) & BM_GPMI_PAYLOAD_RSVD0)
+
+/*============================================================================*/
+
+#define HW_GPMI_AUXILIARY (0x00000050)
+
+#define BP_GPMI_AUXILIARY_ADDRESS 2
+#define BM_GPMI_AUXILIARY_ADDRESS 0xFFFFFFFC
+#define BF_GPMI_AUXILIARY_ADDRESS(v) \
+ (((v) << 2) & BM_GPMI_AUXILIARY_ADDRESS)
+#define BP_GPMI_AUXILIARY_RSVD0 0
+#define BM_GPMI_AUXILIARY_RSVD0 0x00000003
+#define BF_GPMI_AUXILIARY_RSVD0(v) (((v) << 0) & BM_GPMI_AUXILIARY_RSVD0)
+
+/*============================================================================*/
+
+#define HW_GPMI_CTRL1 (0x00000060)
+#define HW_GPMI_CTRL1_SET (0x00000064)
+#define HW_GPMI_CTRL1_CLR (0x00000068)
+#define HW_GPMI_CTRL1_TOG (0x0000006c)
+
+#define BP_GPMI_CTRL1_RSVD2 24
+#define BM_GPMI_CTRL1_RSVD2 0xFF000000
+#define BF_GPMI_CTRL1_RSVD2(v) \
+ (((v) << 24) & BM_GPMI_CTRL1_RSVD2)
+#define BM_GPMI_CTRL1_CE3_SEL 0x00800000
+#define BM_GPMI_CTRL1_CE2_SEL 0x00400000
+#define BM_GPMI_CTRL1_CE1_SEL 0x00200000
+#define BM_GPMI_CTRL1_CE0_SEL 0x00100000
+#define BM_GPMI_CTRL1_GANGED_RDYBUSY 0x00080000
+#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001
+#define BP_GPMI_CTRL1_GPMI_MODE 0
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004
+#define BM_GPMI_CTRL1_DEV_RESET 0x00000008
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200
+#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400
+#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000
+#define BP_GPMI_CTRL1_RDN_DELAY 12
+#define BM_GPMI_CTRL1_BCH_MODE 0x00040000
+#define BP_GPMI_CTRL1_DLL_ENABLE 17
+#define BM_GPMI_CTRL1_DLL_ENABLE 0x00020000
+#define BP_GPMI_CTRL1_HALF_PERIOD 16
+#define BM_GPMI_CTRL1_HALF_PERIOD 0x00010000
+#define BP_GPMI_CTRL1_RDN_DELAY 12
+#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000
+#define BF_GPMI_CTRL1_RDN_DELAY(v) \
+ (((v) << 12) & BM_GPMI_CTRL1_RDN_DELAY)
+#define BM_GPMI_CTRL1_DMA2ECC_MODE 0x00000800
+#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200
+#define BM_GPMI_CTRL1_BURST_EN 0x00000100
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY3 0x00000080
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY2 0x00000040
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY1 0x00000020
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY0 0x00000010
+#define BM_GPMI_CTRL1_DEV_RESET 0x00000008
+#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0
+#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1
+#define BM_GPMI_CTRL1_CAMERA_MODE 0x00000002
+#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING0 (0x00000070)
+
+#define BP_GPMI_TIMING0_RSVD1 24
+#define BM_GPMI_TIMING0_RSVD1 0xFF000000
+#define BF_GPMI_TIMING0_RSVD1(v) \
+ (((v) << 24) & BM_GPMI_TIMING0_RSVD1)
+#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP 0x00FF0000
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \
+ (((v) << 16) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+#define BP_GPMI_TIMING0_DATA_HOLD 8
+#define BM_GPMI_TIMING0_DATA_HOLD 0x0000FF00
+#define BF_GPMI_TIMING0_DATA_HOLD(v) \
+ (((v) << 8) & BM_GPMI_TIMING0_DATA_HOLD)
+#define BP_GPMI_TIMING0_DATA_SETUP 0
+#define BM_GPMI_TIMING0_DATA_SETUP 0x000000FF
+#define BF_GPMI_TIMING0_DATA_SETUP(v) \
+ (((v) << 0) & BM_GPMI_TIMING0_DATA_SETUP)
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING1 (0x00000080)
+
+#define BP_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 16
+#define BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 0xFFFF0000
+#define BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(v) \
+ (((v) << 16) & BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT)
+#define BP_GPMI_TIMING1_RSVD1 0
+#define BM_GPMI_TIMING1_RSVD1 0x0000FFFF
+#define BF_GPMI_TIMING1_RSVD1(v) \
+ (((v) << 0) & BM_GPMI_TIMING1_RSVD1)
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING2 (0x00000090)
+
+#define BP_GPMI_TIMING2_UDMA_TRP 24
+#define BM_GPMI_TIMING2_UDMA_TRP 0xFF000000
+#define BF_GPMI_TIMING2_UDMA_TRP(v) \
+ (((v) << 24) & BM_GPMI_TIMING2_UDMA_TRP)
+#define BP_GPMI_TIMING2_UDMA_ENV 16
+#define BM_GPMI_TIMING2_UDMA_ENV 0x00FF0000
+#define BF_GPMI_TIMING2_UDMA_ENV(v) \
+ (((v) << 16) & BM_GPMI_TIMING2_UDMA_ENV)
+#define BP_GPMI_TIMING2_UDMA_HOLD 8
+#define BM_GPMI_TIMING2_UDMA_HOLD 0x0000FF00
+#define BF_GPMI_TIMING2_UDMA_HOLD(v) \
+ (((v) << 8) & BM_GPMI_TIMING2_UDMA_HOLD)
+#define BP_GPMI_TIMING2_UDMA_SETUP 0
+#define BM_GPMI_TIMING2_UDMA_SETUP 0x000000FF
+#define BF_GPMI_TIMING2_UDMA_SETUP(v) \
+ (((v) << 0) & BM_GPMI_TIMING2_UDMA_SETUP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DATA (0x000000a0)
+
+#define BP_GPMI_DATA_DATA 0
+#define BM_GPMI_DATA_DATA 0xFFFFFFFF
+#define BF_GPMI_DATA_DATA(v) (v)
+
+/*============================================================================*/
+
+#define HW_GPMI_STAT (0x000000b0)
+
+#define BM_GPMI_STAT_PRESENT 0x80000000
+#define BV_GPMI_STAT_PRESENT__UNAVAILABLE 0x0
+#define BV_GPMI_STAT_PRESENT__AVAILABLE 0x1
+#define BP_GPMI_STAT_RSVD1 12
+#define BM_GPMI_STAT_RSVD1 0x7FFFF000
+#define BF_GPMI_STAT_RSVD1(v) \
+ (((v) << 12) & BM_GPMI_STAT_RSVD1)
+#define BP_GPMI_STAT_RDY_TIMEOUT 8
+#define BM_GPMI_STAT_RDY_TIMEOUT 0x00000F00
+#define BF_GPMI_STAT_RDY_TIMEOUT(v) \
+ (((v) << 8) & BM_GPMI_STAT_RDY_TIMEOUT)
+#define BM_GPMI_STAT_ATA_IRQ 0x00000080
+#define BM_GPMI_STAT_INVALID_BUFFER_MASK 0x00000040
+#define BM_GPMI_STAT_FIFO_EMPTY 0x00000020
+#define BV_GPMI_STAT_FIFO_EMPTY__NOT_EMPTY 0x0
+#define BV_GPMI_STAT_FIFO_EMPTY__EMPTY 0x1
+#define BM_GPMI_STAT_FIFO_FULL 0x00000010
+#define BV_GPMI_STAT_FIFO_FULL__NOT_FULL 0x0
+#define BV_GPMI_STAT_FIFO_FULL__FULL 0x1
+#define BM_GPMI_STAT_DEV3_ERROR 0x00000008
+#define BM_GPMI_STAT_DEV2_ERROR 0x00000004
+#define BM_GPMI_STAT_DEV1_ERROR 0x00000002
+#define BM_GPMI_STAT_DEERROR 0x00000001
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG (0x000000c0)
+
+#define BM_GPMI_DEBUG_READY3 0x80000000
+#define BM_GPMI_DEBUG_READY2 0x40000000
+#define BM_GPMI_DEBUG_READY1 0x20000000
+#define BM_GPMI_DEBUG_READY0 0x10000000
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END3 0x08000000
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END2 0x04000000
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END1 0x02000000
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END0 0x01000000
+#define BM_GPMI_DEBUG_SENSE3 0x00800000
+#define BM_GPMI_DEBUG_SENSE2 0x00400000
+#define BM_GPMI_DEBUG_SENSE1 0x00200000
+#define BM_GPMI_DEBUG_SENSE0 0x00100000
+#define BM_GPMI_DEBUG_DMAREQ3 0x00080000
+#define BM_GPMI_DEBUG_DMAREQ2 0x00040000
+#define BM_GPMI_DEBUG_DMAREQ1 0x00020000
+#define BM_GPMI_DEBUG_DMAREQ0 0x00010000
+#define BP_GPMI_DEBUG_CMD_END 12
+#define BM_GPMI_DEBUG_CMD_END 0x0000F000
+#define BF_GPMI_DEBUG_CMD_END(v) \
+ (((v) << 12) & BM_GPMI_DEBUG_CMD_END)
+#define BP_GPMI_DEBUG_UDMA_STATE 8
+#define BM_GPMI_DEBUG_UDMA_STATE 0x00000F00
+#define BF_GPMI_DEBUG_UDMA_STATE(v) \
+ (((v) << 8) & BM_GPMI_DEBUG_UDMA_STATE)
+#define BM_GPMI_DEBUG_BUSY 0x00000080
+#define BV_GPMI_DEBUG_BUSY__DISABLED 0x0
+#define BV_GPMI_DEBUG_BUSY__ENABLED 0x1
+#define BP_GPMI_DEBUG_PIN_STATE 4
+#define BM_GPMI_DEBUG_PIN_STATE 0x00000070
+#define BF_GPMI_DEBUG_PIN_STATE(v) \
+ (((v) << 4) & BM_GPMI_DEBUG_PIN_STATE)
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_IDLE 0x0
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_BYTCNT 0x1
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_ADDR 0x2
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_STALL 0x3
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_STROBE 0x4
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_ATARDY 0x5
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_DHOLD 0x6
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_DONE 0x7
+#define BP_GPMI_DEBUG_MAIN_STATE 0
+#define BM_GPMI_DEBUG_MAIN_STATE 0x0000000F
+#define BF_GPMI_DEBUG_MAIN_STATE(v) \
+ (((v) << 0) & BM_GPMI_DEBUG_MAIN_STATE)
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_IDLE 0x0
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_BYTCNT 0x1
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFE 0x2
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFR 0x3
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DMAREQ 0x4
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DMAACK 0x5
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFF 0x6
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_LDFIFO 0x7
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_LDDMAR 0x8
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_RDCMP 0x9
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DONE 0xA
+
+/*============================================================================*/
+
+#define HW_GPMI_VERSION (0x000000d0)
+
+#define BP_GPMI_VERSION_MAJOR 24
+#define BM_GPMI_VERSION_MAJOR 0xFF000000
+#define BF_GPMI_VERSION_MAJOR(v) (((v) << 24) & BM_GPMI_VERSION_MAJOR)
+#define BP_GPMI_VERSION_MINOR 16
+#define BM_GPMI_VERSION_MINOR 0x00FF0000
+#define BF_GPMI_VERSION_MINOR(v) (((v) << 16) & BM_GPMI_VERSION_MINOR)
+#define BP_GPMI_VERSION_STEP 0
+#define BM_GPMI_VERSION_STEP 0x0000FFFF
+#define BF_GPMI_VERSION_STEP(v) (((v) << 0) & BM_GPMI_VERSION_STEP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG2 (0x000000e0)
+
+#define BP_GPMI_DEBUG2_RSVD1 16
+#define BM_GPMI_DEBUG2_RSVD1 0xFFFF0000
+#define BF_GPMI_DEBUG2_RSVD1(v) (((v) << 16) & BM_GPMI_DEBUG2_RSVD1)
+#define BP_GPMI_DEBUG2_SYND2GPMI_BE 12
+#define BM_GPMI_DEBUG2_SYND2GPMI_BE 0x0000F000
+#define BF_GPMI_DEBUG2_SYND2GPMI_BE(v) \
+ (((v) << 12) & BM_GPMI_DEBUG2_SYND2GPMI_BE)
+#define BM_GPMI_DEBUG2_GPMI2SYND_VALID 0x00000800
+#define BM_GPMI_DEBUG2_GPMI2SYND_READY 0x00000400
+#define BM_GPMI_DEBUG2_SYND2GPMI_VALID 0x00000200
+#define BM_GPMI_DEBUG2_SYND2GPMI_READY 0x00000100
+#define BM_GPMI_DEBUG2_VIEW_DELAYED_RDN 0x00000080
+#define BM_GPMI_DEBUG2_UPDATE_WINDOW 0x00000040
+#define BP_GPMI_DEBUG2_RDN_TAP 0
+#define BM_GPMI_DEBUG2_RDN_TAP 0x0000003F
+#define BF_GPMI_DEBUG2_RDN_TAP(v) (((v) << 0) & BM_GPMI_DEBUG2_RDN_TAP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG3 (0x000000f0)
+
+#define BP_GPMI_DEBUG3_APB_WORD_CNTR 16
+#define BM_GPMI_DEBUG3_APB_WORD_CNTR 0xFFFF0000
+#define BF_GPMI_DEBUG3_APB_WORD_CNTR(v) \
+ (((v) << 16) & BM_GPMI_DEBUG3_APB_WORD_CNTR)
+#define BP_GPMI_DEBUG3_DEV_WORD_CNTR 0
+#define BM_GPMI_DEBUG3_DEV_WORD_CNTR 0x0000FFFF
+#define BF_GPMI_DEBUG3_DEV_WORD_CNTR(v) \
+ (((v) << 0) & BM_GPMI_DEBUG3_DEV_WORD_CNTR)
+
+/*============================================================================*/
+#endif
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h
new file mode 100644
index 000000000000..dcb3b7d3fc88
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v1.h
@@ -0,0 +1,421 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Xml Revision: 2.2
+ * Template revision: 26195
+ */
+
+#ifndef __GPMI_NFC_GPMI_REGS_H
+#define __GPMI_NFC_GPMI_REGS_H
+
+/*============================================================================*/
+
+#define HW_GPMI_CTRL0 (0x00000000)
+#define HW_GPMI_CTRL0_SET (0x00000004)
+#define HW_GPMI_CTRL0_CLR (0x00000008)
+#define HW_GPMI_CTRL0_TOG (0x0000000c)
+
+#define BM_GPMI_CTRL0_SFTRST 0x80000000
+#define BV_GPMI_CTRL0_SFTRST__RUN 0x0
+#define BV_GPMI_CTRL0_SFTRST__RESET 0x1
+#define BM_GPMI_CTRL0_CLKGATE 0x40000000
+#define BV_GPMI_CTRL0_CLKGATE__RUN 0x0
+#define BV_GPMI_CTRL0_CLKGATE__NO_CLKS 0x1
+#define BM_GPMI_CTRL0_RUN 0x20000000
+#define BV_GPMI_CTRL0_RUN__IDLE 0x0
+#define BV_GPMI_CTRL0_RUN__BUSY 0x1
+#define BM_GPMI_CTRL0_DEV_IRQ_EN 0x10000000
+#define BM_GPMI_CTRL0_LOCK_CS 0x08000000
+#define BV_GPMI_CTRL0_LOCK_CS__DISABLED 0x0
+#define BV_GPMI_CTRL0_LOCK_CS__ENABLED 0x1
+#define BM_GPMI_CTRL0_UDMA 0x04000000
+#define BV_GPMI_CTRL0_UDMA__DISABLED 0x0
+#define BV_GPMI_CTRL0_UDMA__ENABLED 0x1
+#define BP_GPMI_CTRL0_COMMAND_MODE 24
+#define BM_GPMI_CTRL0_COMMAND_MODE 0x03000000
+#define BF_GPMI_CTRL0_COMMAND_MODE(v) \
+ (((v) << 24) & BM_GPMI_CTRL0_COMMAND_MODE)
+#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2
+#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3
+#define BM_GPMI_CTRL0_WORD_LENGTH 0x00800000
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1
+#define BP_GPMI_CTRL0_CS 20
+#define BM_GPMI_CTRL0_CS 0x00700000
+#define BF_GPMI_CTRL0_CS(v) \
+ (((v) << 20) & BM_GPMI_CTRL0_CS)
+#define BP_GPMI_CTRL0_ADDRESS 17
+#define BM_GPMI_CTRL0_ADDRESS 0x000E0000
+#define BF_GPMI_CTRL0_ADDRESS(v) \
+ (((v) << 17) & BM_GPMI_CTRL0_ADDRESS)
+#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0
+#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1
+#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2
+#define BM_GPMI_CTRL0_ADDRESS_INCREMENT 0x00010000
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1
+#define BP_GPMI_CTRL0_XFER_COUNT 0
+#define BM_GPMI_CTRL0_XFER_COUNT 0x0000FFFF
+#define BF_GPMI_CTRL0_XFER_COUNT(v) \
+ (((v) << 0) & BM_GPMI_CTRL0_XFER_COUNT)
+
+/*============================================================================*/
+
+#define HW_GPMI_COMPARE (0x00000010)
+
+#define BP_GPMI_COMPARE_MASK 16
+#define BM_GPMI_COMPARE_MASK 0xFFFF0000
+#define BF_GPMI_COMPARE_MASK(v) \
+ (((v) << 16) & BM_GPMI_COMPARE_MASK)
+#define BP_GPMI_COMPARE_REFERENCE 0
+#define BM_GPMI_COMPARE_REFERENCE 0x0000FFFF
+#define BF_GPMI_COMPARE_REFERENCE(v) \
+ (((v) << 0) & BM_GPMI_COMPARE_REFERENCE)
+
+/*============================================================================*/
+
+#define HW_GPMI_ECCCTRL (0x00000020)
+#define HW_GPMI_ECCCTRL_SET (0x00000024)
+#define HW_GPMI_ECCCTRL_CLR (0x00000028)
+#define HW_GPMI_ECCCTRL_TOG (0x0000002c)
+
+#define BP_GPMI_ECCCTRL_HANDLE 16
+#define BM_GPMI_ECCCTRL_HANDLE 0xFFFF0000
+#define BF_GPMI_ECCCTRL_HANDLE(v) \
+ (((v) << 16) & BM_GPMI_ECCCTRL_HANDLE)
+#define BM_GPMI_ECCCTRL_RSVD2 0x00008000
+#define BP_GPMI_ECCCTRL_ECC_CMD 13
+#define BM_GPMI_ECCCTRL_ECC_CMD 0x00006000
+#define BF_GPMI_ECCCTRL_ECC_CMD(v) \
+ (((v) << 13) & BM_GPMI_ECCCTRL_ECC_CMD)
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE 0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE 0x1
+#define BV_GPMI_ECCCTRL_ECC_CMD__RESERVE2 0x2
+#define BV_GPMI_ECCCTRL_ECC_CMD__RESERVE3 0x3
+#define BM_GPMI_ECCCTRL_ENABLE_ECC 0x00001000
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0
+#define BP_GPMI_ECCCTRL_RSVD1 9
+#define BM_GPMI_ECCCTRL_RSVD1 0x00000E00
+#define BF_GPMI_ECCCTRL_RSVD1(v) \
+ (((v) << 9) & BM_GPMI_ECCCTRL_RSVD1)
+#define BP_GPMI_ECCCTRL_BUFFER_MASK 0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK 0x000001FF
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \
+ (((v) << 0) & BM_GPMI_ECCCTRL_BUFFER_MASK)
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF
+
+/*============================================================================*/
+
+#define HW_GPMI_ECCCOUNT (0x00000030)
+
+#define BP_GPMI_ECCCOUNT_RSVD2 16
+#define BM_GPMI_ECCCOUNT_RSVD2 0xFFFF0000
+#define BF_GPMI_ECCCOUNT_RSVD2(v) \
+ (((v) << 16) & BM_GPMI_ECCCOUNT_RSVD2)
+#define BP_GPMI_ECCCOUNT_COUNT 0
+#define BM_GPMI_ECCCOUNT_COUNT 0x0000FFFF
+#define BF_GPMI_ECCCOUNT_COUNT(v) \
+ (((v) << 0) & BM_GPMI_ECCCOUNT_COUNT)
+
+/*============================================================================*/
+
+#define HW_GPMI_PAYLOAD (0x00000040)
+
+#define BP_GPMI_PAYLOAD_ADDRESS 2
+#define BM_GPMI_PAYLOAD_ADDRESS 0xFFFFFFFC
+#define BF_GPMI_PAYLOAD_ADDRESS(v) \
+ (((v) << 2) & BM_GPMI_PAYLOAD_ADDRESS)
+#define BP_GPMI_PAYLOAD_RSVD0 0
+#define BM_GPMI_PAYLOAD_RSVD0 0x00000003
+#define BF_GPMI_PAYLOAD_RSVD0(v) \
+ (((v) << 0) & BM_GPMI_PAYLOAD_RSVD0)
+
+/*============================================================================*/
+
+#define HW_GPMI_AUXILIARY (0x00000050)
+
+#define BP_GPMI_AUXILIARY_ADDRESS 2
+#define BM_GPMI_AUXILIARY_ADDRESS 0xFFFFFFFC
+#define BF_GPMI_AUXILIARY_ADDRESS(v) \
+ (((v) << 2) & BM_GPMI_AUXILIARY_ADDRESS)
+#define BP_GPMI_AUXILIARY_RSVD0 0
+#define BM_GPMI_AUXILIARY_RSVD0 0x00000003
+#define BF_GPMI_AUXILIARY_RSVD0(v) \
+ (((v) << 0) & BM_GPMI_AUXILIARY_RSVD0)
+
+/*============================================================================*/
+
+#define HW_GPMI_CTRL1 (0x00000060)
+#define HW_GPMI_CTRL1_SET (0x00000064)
+#define HW_GPMI_CTRL1_CLR (0x00000068)
+#define HW_GPMI_CTRL1_TOG (0x0000006c)
+
+#define BP_GPMI_CTRL1_RSVD2 25
+#define BM_GPMI_CTRL1_RSVD2 0xFE000000
+#define BF_GPMI_CTRL1_RSVD2(v) \
+ (((v) << 25) & BM_GPMI_CTRL1_RSVD2)
+#define BM_GPMI_CTRL1_DECOUPLE_CS 0x01000000
+#define BP_GPMI_CTRL1_WRN_DLY_SEL 22
+#define BM_GPMI_CTRL1_WRN_DLY_SEL 0x00C00000
+#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \
+ (((v) << 22) & BM_GPMI_CTRL1_WRN_DLY_SEL)
+#define BM_GPMI_CTRL1_RSVD1 0x00200000
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ_EN 0x00100000
+#define BM_GPMI_CTRL1_GANGED_RDYBUSY 0x00080000
+#define BM_GPMI_CTRL1_BCH_MODE 0x00040000
+#define BP_GPMI_CTRL1_DLL_ENABLE 17
+#define BM_GPMI_CTRL1_DLL_ENABLE 0x00020000
+#define BP_GPMI_CTRL1_HALF_PERIOD 16
+#define BM_GPMI_CTRL1_HALF_PERIOD 0x00010000
+#define BP_GPMI_CTRL1_RDN_DELAY 12
+#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000
+#define BF_GPMI_CTRL1_RDN_DELAY(v) \
+ (((v) << 12) & BM_GPMI_CTRL1_RDN_DELAY)
+#define BM_GPMI_CTRL1_DMA2ECC_MODE 0x00000800
+#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200
+#define BM_GPMI_CTRL1_BURST_EN 0x00000100
+#define BM_GPMI_CTRL1_ABORT_WAIT_REQUEST 0x00000080
+#define BP_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL 4
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL 0x00000070
+#define BF_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL(v) \
+ (((v) << 4) & BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL)
+#define BM_GPMI_CTRL1_DEV_RESET 0x00000008
+#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0
+#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1
+#define BM_GPMI_CTRL1_CAMERA_MODE 0x00000002
+#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING0 (0x00000070)
+
+#define BP_GPMI_TIMING0_RSVD1 24
+#define BM_GPMI_TIMING0_RSVD1 0xFF000000
+#define BF_GPMI_TIMING0_RSVD1(v) \
+ (((v) << 24) & BM_GPMI_TIMING0_RSVD1)
+#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP 0x00FF0000
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \
+ (((v) << 16) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+#define BP_GPMI_TIMING0_DATA_HOLD 8
+#define BM_GPMI_TIMING0_DATA_HOLD 0x0000FF00
+#define BF_GPMI_TIMING0_DATA_HOLD(v) \
+ (((v) << 8) & BM_GPMI_TIMING0_DATA_HOLD)
+#define BP_GPMI_TIMING0_DATA_SETUP 0
+#define BM_GPMI_TIMING0_DATA_SETUP 0x000000FF
+#define BF_GPMI_TIMING0_DATA_SETUP(v) \
+ (((v) << 0) & BM_GPMI_TIMING0_DATA_SETUP)
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING1 (0x00000080)
+
+#define BP_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 16
+#define BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 0xFFFF0000
+#define BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(v) \
+ (((v) << 16) & BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT)
+#define BP_GPMI_TIMING1_RSVD1 0
+#define BM_GPMI_TIMING1_RSVD1 0x0000FFFF
+#define BF_GPMI_TIMING1_RSVD1(v) \
+ (((v) << 0) & BM_GPMI_TIMING1_RSVD1)
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING2 (0x00000090)
+
+#define BP_GPMI_TIMING2_UDMA_TRP 24
+#define BM_GPMI_TIMING2_UDMA_TRP 0xFF000000
+#define BF_GPMI_TIMING2_UDMA_TRP(v) \
+ (((v) << 24) & BM_GPMI_TIMING2_UDMA_TRP)
+#define BP_GPMI_TIMING2_UDMA_ENV 16
+#define BM_GPMI_TIMING2_UDMA_ENV 0x00FF0000
+#define BF_GPMI_TIMING2_UDMA_ENV(v) \
+ (((v) << 16) & BM_GPMI_TIMING2_UDMA_ENV)
+#define BP_GPMI_TIMING2_UDMA_HOLD 8
+#define BM_GPMI_TIMING2_UDMA_HOLD 0x0000FF00
+#define BF_GPMI_TIMING2_UDMA_HOLD(v) \
+ (((v) << 8) & BM_GPMI_TIMING2_UDMA_HOLD)
+#define BP_GPMI_TIMING2_UDMA_SETUP 0
+#define BM_GPMI_TIMING2_UDMA_SETUP 0x000000FF
+#define BF_GPMI_TIMING2_UDMA_SETUP(v) \
+ (((v) << 0) & BM_GPMI_TIMING2_UDMA_SETUP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DATA (0x000000a0)
+
+#define BP_GPMI_DATA_DATA 0
+#define BM_GPMI_DATA_DATA 0xFFFFFFFF
+#define BF_GPMI_DATA_DATA(v) (v)
+
+#define HW_GPMI_STAT (0x000000b0)
+
+#define BP_GPMI_STAT_READY_BUSY 24
+#define BM_GPMI_STAT_READY_BUSY 0xFF000000
+#define BF_GPMI_STAT_READY_BUSY(v) \
+ (((v) << 24) & BM_GPMI_STAT_READY_BUSY)
+#define BP_GPMI_STAT_RDY_TIMEOUT 16
+#define BM_GPMI_STAT_RDY_TIMEOUT 0x00FF0000
+#define BF_GPMI_STAT_RDY_TIMEOUT(v) \
+ (((v) << 16) & BM_GPMI_STAT_RDY_TIMEOUT)
+#define BM_GPMI_STAT_DEV7_ERROR 0x00008000
+#define BM_GPMI_STAT_DEV6_ERROR 0x00004000
+#define BM_GPMI_STAT_DEV5_ERROR 0x00002000
+#define BM_GPMI_STAT_DEV4_ERROR 0x00001000
+#define BM_GPMI_STAT_DEV3_ERROR 0x00000800
+#define BM_GPMI_STAT_DEV2_ERROR 0x00000400
+#define BM_GPMI_STAT_DEERROR 0x00000200
+#define BM_GPMI_STAT_DEV0_ERROR 0x00000100
+#define BP_GPMI_STAT_RSVD1 5
+#define BM_GPMI_STAT_RSVD1 0x000000E0
+#define BF_GPMI_STAT_RSVD1(v) \
+ (((v) << 5) & BM_GPMI_STAT_RSVD1)
+#define BM_GPMI_STAT_ATA_IRQ 0x00000010
+#define BM_GPMI_STAT_INVALID_BUFFER_MASK 0x00000008
+#define BM_GPMI_STAT_FIFO_EMPTY 0x00000004
+#define BV_GPMI_STAT_FIFO_EMPTY__NOT_EMPTY 0x0
+#define BV_GPMI_STAT_FIFO_EMPTY__EMPTY 0x1
+#define BM_GPMI_STAT_FIFO_FULL 0x00000002
+#define BV_GPMI_STAT_FIFO_FULL__NOT_FULL 0x0
+#define BV_GPMI_STAT_FIFO_FULL__FULL 0x1
+#define BM_GPMI_STAT_PRESENT 0x00000001
+#define BV_GPMI_STAT_PRESENT__UNAVAILABLE 0x0
+#define BV_GPMI_STAT_PRESENT__AVAILABLE 0x1
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG (0x000000c0)
+
+#define BP_GPMI_DEBUG_WAIT_FOR_READY_END 24
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END 0xFF000000
+#define BF_GPMI_DEBUG_WAIT_FOR_READY_END(v) \
+ (((v) << 24) & BM_GPMI_DEBUG_WAIT_FOR_READY_END)
+#define BP_GPMI_DEBUG_DMA_SENSE 16
+#define BM_GPMI_DEBUG_DMA_SENSE 0x00FF0000
+#define BF_GPMI_DEBUG_DMA_SENSE(v) \
+ (((v) << 16) & BM_GPMI_DEBUG_DMA_SENSE)
+#define BP_GPMI_DEBUG_DMAREQ 8
+#define BM_GPMI_DEBUG_DMAREQ 0x0000FF00
+#define BF_GPMI_DEBUG_DMAREQ(v) \
+ (((v) << 8) & BM_GPMI_DEBUG_DMAREQ)
+#define BP_GPMI_DEBUG_CMD_END 0
+#define BM_GPMI_DEBUG_CMD_END 0x000000FF
+#define BF_GPMI_DEBUG_CMD_END(v) \
+ (((v) << 0) & BM_GPMI_DEBUG_CMD_END)
+
+/*============================================================================*/
+
+#define HW_GPMI_VERSION (0x000000d0)
+
+#define BP_GPMI_VERSION_MAJOR 24
+#define BM_GPMI_VERSION_MAJOR 0xFF000000
+#define BF_GPMI_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_GPMI_VERSION_MAJOR)
+#define BP_GPMI_VERSION_MINOR 16
+#define BM_GPMI_VERSION_MINOR 0x00FF0000
+#define BF_GPMI_VERSION_MINOR(v) \
+ (((v) << 16) & BM_GPMI_VERSION_MINOR)
+#define BP_GPMI_VERSION_STEP 0
+#define BM_GPMI_VERSION_STEP 0x0000FFFF
+#define BF_GPMI_VERSION_STEP(v) \
+ (((v) << 0) & BM_GPMI_VERSION_STEP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG2 (0x000000e0)
+
+#define BP_GPMI_DEBUG2_RSVD1 28
+#define BM_GPMI_DEBUG2_RSVD1 0xF0000000
+#define BF_GPMI_DEBUG2_RSVD1(v) \
+ (((v) << 28) & BM_GPMI_DEBUG2_RSVD1)
+#define BP_GPMI_DEBUG2_UDMA_STATE 24
+#define BM_GPMI_DEBUG2_UDMA_STATE 0x0F000000
+#define BF_GPMI_DEBUG2_UDMA_STATE(v) \
+ (((v) << 24) & BM_GPMI_DEBUG2_UDMA_STATE)
+#define BM_GPMI_DEBUG2_BUSY 0x00800000
+#define BV_GPMI_DEBUG2_BUSY__DISABLED 0x0
+#define BV_GPMI_DEBUG2_BUSY__ENABLED 0x1
+#define BP_GPMI_DEBUG2_PIN_STATE 20
+#define BM_GPMI_DEBUG2_PIN_STATE 0x00700000
+#define BF_GPMI_DEBUG2_PIN_STATE(v) \
+ (((v) << 20) & BM_GPMI_DEBUG2_PIN_STATE)
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_IDLE 0x0
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_BYTCNT 0x1
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_ADDR 0x2
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_STALL 0x3
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_STROBE 0x4
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_ATARDY 0x5
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_DHOLD 0x6
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_DONE 0x7
+#define BP_GPMI_DEBUG2_MAIN_STATE 16
+#define BM_GPMI_DEBUG2_MAIN_STATE 0x000F0000
+#define BF_GPMI_DEBUG2_MAIN_STATE(v) \
+ (((v) << 16) & BM_GPMI_DEBUG2_MAIN_STATE)
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_IDLE 0x0
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_BYTCNT 0x1
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFE 0x2
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFR 0x3
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DMAREQ 0x4
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DMAACK 0x5
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFF 0x6
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_LDFIFO 0x7
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_LDDMAR 0x8
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_RDCMP 0x9
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DONE 0xA
+#define BP_GPMI_DEBUG2_SYND2GPMI_BE 12
+#define BM_GPMI_DEBUG2_SYND2GPMI_BE 0x0000F000
+#define BF_GPMI_DEBUG2_SYND2GPMI_BE(v) \
+ (((v) << 12) & BM_GPMI_DEBUG2_SYND2GPMI_BE)
+#define BM_GPMI_DEBUG2_GPMI2SYND_VALID 0x00000800
+#define BM_GPMI_DEBUG2_GPMI2SYND_READY 0x00000400
+#define BM_GPMI_DEBUG2_SYND2GPMI_VALID 0x00000200
+#define BM_GPMI_DEBUG2_SYND2GPMI_READY 0x00000100
+#define BM_GPMI_DEBUG2_VIEW_DELAYED_RDN 0x00000080
+#define BM_GPMI_DEBUG2_UPDATE_WINDOW 0x00000040
+#define BP_GPMI_DEBUG2_RDN_TAP 0
+#define BM_GPMI_DEBUG2_RDN_TAP 0x0000003F
+#define BF_GPMI_DEBUG2_RDN_TAP(v) \
+ (((v) << 0) & BM_GPMI_DEBUG2_RDN_TAP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG3 (0x000000f0)
+
+#define BP_GPMI_DEBUG3_APB_WORD_CNTR 16
+#define BM_GPMI_DEBUG3_APB_WORD_CNTR 0xFFFF0000
+#define BF_GPMI_DEBUG3_APB_WORD_CNTR(v) \
+ (((v) << 16) & BM_GPMI_DEBUG3_APB_WORD_CNTR)
+#define BP_GPMI_DEBUG3_DEV_WORD_CNTR 0
+#define BM_GPMI_DEBUG3_DEV_WORD_CNTR 0x0000FFFF
+#define BF_GPMI_DEBUG3_DEV_WORD_CNTR(v) \
+ (((v) << 0) & BM_GPMI_DEBUG3_DEV_WORD_CNTR)
+
+/*============================================================================*/
+
+#endif
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v2.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v2.h
new file mode 100644
index 000000000000..3baa9012da69
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-gpmi-regs-v2.h
@@ -0,0 +1,511 @@
+/*
+ * Freescale GPMI Register Definitions
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This file is created by xml file. Don't Edit it.
+ *
+ * Xml Revision: 1.19
+ * Template revision: 1.3
+ */
+
+#ifndef __ARCH_ARM___GPMI_H
+#define __ARCH_ARM___GPMI_H
+
+
+#define HW_GPMI_CTRL0 (0x00000000)
+#define HW_GPMI_CTRL0_SET (0x00000004)
+#define HW_GPMI_CTRL0_CLR (0x00000008)
+#define HW_GPMI_CTRL0_TOG (0x0000000c)
+
+#define BM_GPMI_CTRL0_SFTRST 0x80000000
+#define BV_GPMI_CTRL0_SFTRST__RUN 0x0
+#define BV_GPMI_CTRL0_SFTRST__RESET 0x1
+#define BM_GPMI_CTRL0_CLKGATE 0x40000000
+#define BV_GPMI_CTRL0_CLKGATE__RUN 0x0
+#define BV_GPMI_CTRL0_CLKGATE__NO_CLKS 0x1
+#define BM_GPMI_CTRL0_RUN 0x20000000
+#define BV_GPMI_CTRL0_RUN__IDLE 0x0
+#define BV_GPMI_CTRL0_RUN__BUSY 0x1
+#define BM_GPMI_CTRL0_DEV_IRQ_EN 0x10000000
+#define BM_GPMI_CTRL0_LOCK_CS 0x08000000
+#define BV_GPMI_CTRL0_LOCK_CS__DISABLED 0x0
+#define BV_GPMI_CTRL0_LOCK_CS__ENABLED 0x1
+#define BM_GPMI_CTRL0_UDMA 0x04000000
+#define BV_GPMI_CTRL0_UDMA__DISABLED 0x0
+#define BV_GPMI_CTRL0_UDMA__ENABLED 0x1
+#define BP_GPMI_CTRL0_COMMAND_MODE 24
+#define BM_GPMI_CTRL0_COMMAND_MODE 0x03000000
+#define BF_GPMI_CTRL0_COMMAND_MODE(v) \
+ (((v) << 24) & BM_GPMI_CTRL0_COMMAND_MODE)
+#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2
+#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3
+#define BM_GPMI_CTRL0_WORD_LENGTH 0x00800000
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1
+#define BP_GPMI_CTRL0_CS 20
+#define BM_GPMI_CTRL0_CS 0x00700000
+#define BF_GPMI_CTRL0_CS(v) \
+ (((v) << 20) & BM_GPMI_CTRL0_CS)
+#define BP_GPMI_CTRL0_ADDRESS 17
+#define BM_GPMI_CTRL0_ADDRESS 0x000E0000
+#define BF_GPMI_CTRL0_ADDRESS(v) \
+ (((v) << 17) & BM_GPMI_CTRL0_ADDRESS)
+#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0
+#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1
+#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2
+#define BM_GPMI_CTRL0_ADDRESS_INCREMENT 0x00010000
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1
+#define BP_GPMI_CTRL0_XFER_COUNT 0
+#define BM_GPMI_CTRL0_XFER_COUNT 0x0000FFFF
+#define BF_GPMI_CTRL0_XFER_COUNT(v) \
+ (((v) << 0) & BM_GPMI_CTRL0_XFER_COUNT)
+
+#define HW_GPMI_COMPARE (0x00000010)
+
+#define BP_GPMI_COMPARE_MASK 16
+#define BM_GPMI_COMPARE_MASK 0xFFFF0000
+#define BF_GPMI_COMPARE_MASK(v) \
+ (((v) << 16) & BM_GPMI_COMPARE_MASK)
+#define BP_GPMI_COMPARE_REFERENCE 0
+#define BM_GPMI_COMPARE_REFERENCE 0x0000FFFF
+#define BF_GPMI_COMPARE_REFERENCE(v) \
+ (((v) << 0) & BM_GPMI_COMPARE_REFERENCE)
+
+#define HW_GPMI_ECCCTRL (0x00000020)
+#define HW_GPMI_ECCCTRL_SET (0x00000024)
+#define HW_GPMI_ECCCTRL_CLR (0x00000028)
+#define HW_GPMI_ECCCTRL_TOG (0x0000002c)
+
+#define BP_GPMI_ECCCTRL_HANDLE 16
+#define BM_GPMI_ECCCTRL_HANDLE 0xFFFF0000
+#define BF_GPMI_ECCCTRL_HANDLE(v) \
+ (((v) << 16) & BM_GPMI_ECCCTRL_HANDLE)
+#define BM_GPMI_ECCCTRL_RSVD2 0x00008000
+#define BP_GPMI_ECCCTRL_ECC_CMD 13
+#define BM_GPMI_ECCCTRL_ECC_CMD 0x00006000
+#define BF_GPMI_ECCCTRL_ECC_CMD(v) \
+ (((v) << 13) & BM_GPMI_ECCCTRL_ECC_CMD)
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE 0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE 0x1
+#define BV_GPMI_ECCCTRL_ECC_CMD__RESERVE2 0x2
+#define BV_GPMI_ECCCTRL_ECC_CMD__RESERVE3 0x3
+#define BM_GPMI_ECCCTRL_ENABLE_ECC 0x00001000
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0
+#define BP_GPMI_ECCCTRL_RSVD1 9
+#define BM_GPMI_ECCCTRL_RSVD1 0x00000E00
+#define BF_GPMI_ECCCTRL_RSVD1(v) \
+ (((v) << 9) & BM_GPMI_ECCCTRL_RSVD1)
+#define BP_GPMI_ECCCTRL_BUFFER_MASK 0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK 0x000001FF
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \
+ (((v) << 0) & BM_GPMI_ECCCTRL_BUFFER_MASK)
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF
+
+#define HW_GPMI_ECCCOUNT (0x00000030)
+
+#define BP_GPMI_ECCCOUNT_RSVD2 16
+#define BM_GPMI_ECCCOUNT_RSVD2 0xFFFF0000
+#define BF_GPMI_ECCCOUNT_RSVD2(v) \
+ (((v) << 16) & BM_GPMI_ECCCOUNT_RSVD2)
+#define BP_GPMI_ECCCOUNT_COUNT 0
+#define BM_GPMI_ECCCOUNT_COUNT 0x0000FFFF
+#define BF_GPMI_ECCCOUNT_COUNT(v) \
+ (((v) << 0) & BM_GPMI_ECCCOUNT_COUNT)
+
+#define HW_GPMI_PAYLOAD (0x00000040)
+
+#define BP_GPMI_PAYLOAD_ADDRESS 2
+#define BM_GPMI_PAYLOAD_ADDRESS 0xFFFFFFFC
+#define BF_GPMI_PAYLOAD_ADDRESS(v) \
+ (((v) << 2) & BM_GPMI_PAYLOAD_ADDRESS)
+#define BP_GPMI_PAYLOAD_RSVD0 0
+#define BM_GPMI_PAYLOAD_RSVD0 0x00000003
+#define BF_GPMI_PAYLOAD_RSVD0(v) \
+ (((v) << 0) & BM_GPMI_PAYLOAD_RSVD0)
+
+#define HW_GPMI_AUXILIARY (0x00000050)
+
+#define BP_GPMI_AUXILIARY_ADDRESS 2
+#define BM_GPMI_AUXILIARY_ADDRESS 0xFFFFFFFC
+#define BF_GPMI_AUXILIARY_ADDRESS(v) \
+ (((v) << 2) & BM_GPMI_AUXILIARY_ADDRESS)
+#define BP_GPMI_AUXILIARY_RSVD0 0
+#define BM_GPMI_AUXILIARY_RSVD0 0x00000003
+#define BF_GPMI_AUXILIARY_RSVD0(v) \
+ (((v) << 0) & BM_GPMI_AUXILIARY_RSVD0)
+
+#define HW_GPMI_CTRL1 (0x00000060)
+#define HW_GPMI_CTRL1_SET (0x00000064)
+#define HW_GPMI_CTRL1_CLR (0x00000068)
+#define HW_GPMI_CTRL1_TOG (0x0000006c)
+
+#define BM_GPMI_CTRL1_DEV_CLK_STOP 0x80000000
+#define BM_GPMI_CTRL1_SSYNC_CLK_STOP 0x40000000
+#define BM_GPMI_CTRL1_WRITE_CLK_STOP 0x20000000
+#define BM_GPMI_CTRL1_TOGGLE_MODE 0x10000000
+#define BM_GPMI_CTRL1_GPMI_CLK_DIV2_EN 0x08000000
+#define BM_GPMI_CTRL1_UPDATE_CS 0x04000000
+#define BM_GPMI_CTRL1_SSYNCMODE 0x02000000
+#define BV_GPMI_CTRL1_SSYNCMODE__ASYNC 0x0
+#define BV_GPMI_CTRL1_SSYNCMODE__SSYNC 0x1
+#define BM_GPMI_CTRL1_DECOUPLE_CS 0x01000000
+#define BP_GPMI_CTRL1_WRN_DLY_SEL 22
+#define BM_GPMI_CTRL1_WRN_DLY_SEL 0x00C00000
+#define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \
+ (((v) << 22) & BM_GPMI_CTRL1_WRN_DLY_SEL)
+#define BM_GPMI_CTRL1_RSVD1 0x00200000
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ_EN 0x00100000
+#define BM_GPMI_CTRL1_GANGED_RDYBUSY 0x00080000
+#define BM_GPMI_CTRL1_BCH_MODE 0x00040000
+#define BM_GPMI_CTRL1_DLL_ENABLE 0x00020000
+#define BP_GPMI_CTRL1_HALF_PERIOD 16
+#define BM_GPMI_CTRL1_HALF_PERIOD 0x00010000
+#define BP_GPMI_CTRL1_RDN_DELAY 12
+#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000
+#define BF_GPMI_CTRL1_RDN_DELAY(v) \
+ (((v) << 12) & BM_GPMI_CTRL1_RDN_DELAY)
+#define BM_GPMI_CTRL1_DMA2ECC_MODE 0x00000800
+#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200
+#define BM_GPMI_CTRL1_BURST_EN 0x00000100
+#define BM_GPMI_CTRL1_ABORT_WAIT_REQUEST 0x00000080
+#define BP_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL 4
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL 0x00000070
+#define BF_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL(v) \
+ (((v) << 4) & BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL)
+#define BM_GPMI_CTRL1_DEV_RESET 0x00000008
+#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0
+#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1
+#define BM_GPMI_CTRL1_CAMERA_MODE 0x00000002
+#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1
+
+#define HW_GPMI_TIMING0 (0x00000070)
+
+#define BP_GPMI_TIMING0_RSVD1 24
+#define BM_GPMI_TIMING0_RSVD1 0xFF000000
+#define BF_GPMI_TIMING0_RSVD1(v) \
+ (((v) << 24) & BM_GPMI_TIMING0_RSVD1)
+#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP 0x00FF0000
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \
+ (((v) << 16) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+#define BP_GPMI_TIMING0_DATA_HOLD 8
+#define BM_GPMI_TIMING0_DATA_HOLD 0x0000FF00
+#define BF_GPMI_TIMING0_DATA_HOLD(v) \
+ (((v) << 8) & BM_GPMI_TIMING0_DATA_HOLD)
+#define BP_GPMI_TIMING0_DATA_SETUP 0
+#define BM_GPMI_TIMING0_DATA_SETUP 0x000000FF
+#define BF_GPMI_TIMING0_DATA_SETUP(v) \
+ (((v) << 0) & BM_GPMI_TIMING0_DATA_SETUP)
+
+#define HW_GPMI_TIMING1 (0x00000080)
+
+#define BP_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 16
+#define BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 0xFFFF0000
+#define BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(v) \
+ (((v) << 16) & BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT)
+#define BP_GPMI_TIMING1_RSVD1 0
+#define BM_GPMI_TIMING1_RSVD1 0x0000FFFF
+#define BF_GPMI_TIMING1_RSVD1(v) \
+ (((v) << 0) & BM_GPMI_TIMING1_RSVD1)
+
+#define HW_GPMI_TIMING2 (0x00000090)
+
+#define BP_GPMI_TIMING2_RSVD1 27
+#define BM_GPMI_TIMING2_RSVD1 0xF8000000
+#define BF_GPMI_TIMING2_RSVD1(v) \
+ (((v) << 27) & BM_GPMI_TIMING2_RSVD1)
+#define BP_GPMI_TIMING2_READ_LATENCY 24
+#define BM_GPMI_TIMING2_READ_LATENCY 0x07000000
+#define BF_GPMI_TIMING2_READ_LATENCY(v) \
+ (((v) << 24) & BM_GPMI_TIMING2_READ_LATENCY)
+#define BP_GPMI_TIMING2_RSVD0 21
+#define BM_GPMI_TIMING2_RSVD0 0x00E00000
+#define BF_GPMI_TIMING2_RSVD0(v) \
+ (((v) << 21) & BM_GPMI_TIMING2_RSVD0)
+#define BP_GPMI_TIMING2_CE_DELAY 16
+#define BM_GPMI_TIMING2_CE_DELAY 0x001F0000
+#define BF_GPMI_TIMING2_CE_DELAY(v) \
+ (((v) << 16) & BM_GPMI_TIMING2_CE_DELAY)
+#define BP_GPMI_TIMING2_PREAMBLE_DELAY 12
+#define BM_GPMI_TIMING2_PREAMBLE_DELAY 0x0000F000
+#define BF_GPMI_TIMING2_PREAMBLE_DELAY(v) \
+ (((v) << 12) & BM_GPMI_TIMING2_PREAMBLE_DELAY)
+#define BP_GPMI_TIMING2_POSTAMBLE_DELAY 8
+#define BM_GPMI_TIMING2_POSTAMBLE_DELAY 0x00000F00
+#define BF_GPMI_TIMING2_POSTAMBLE_DELAY(v) \
+ (((v) << 8) & BM_GPMI_TIMING2_POSTAMBLE_DELAY)
+#define BP_GPMI_TIMING2_CMDADD_PAUSE 4
+#define BM_GPMI_TIMING2_CMDADD_PAUSE 0x000000F0
+#define BF_GPMI_TIMING2_CMDADD_PAUSE(v) \
+ (((v) << 4) & BM_GPMI_TIMING2_CMDADD_PAUSE)
+#define BP_GPMI_TIMING2_DATA_PAUSE 0
+#define BM_GPMI_TIMING2_DATA_PAUSE 0x0000000F
+#define BF_GPMI_TIMING2_DATA_PAUSE(v) \
+ (((v) << 0) & BM_GPMI_TIMING2_DATA_PAUSE)
+
+#define HW_GPMI_DATA (0x000000a0)
+
+#define BP_GPMI_DATA_DATA 0
+#define BM_GPMI_DATA_DATA 0xFFFFFFFF
+#define BF_GPMI_DATA_DATA(v) (v)
+
+#define HW_GPMI_STAT (0x000000b0)
+
+#define BP_GPMI_STAT_READY_BUSY 24
+#define BM_GPMI_STAT_READY_BUSY 0xFF000000
+#define BF_GPMI_STAT_READY_BUSY(v) \
+ (((v) << 24) & BM_GPMI_STAT_READY_BUSY)
+#define BP_GPMI_STAT_RDY_TIMEOUT 16
+#define BM_GPMI_STAT_RDY_TIMEOUT 0x00FF0000
+#define BF_GPMI_STAT_RDY_TIMEOUT(v) \
+ (((v) << 16) & BM_GPMI_STAT_RDY_TIMEOUT)
+#define BM_GPMI_STAT_DEV7_ERROR 0x00008000
+#define BM_GPMI_STAT_DEV6_ERROR 0x00004000
+#define BM_GPMI_STAT_DEV5_ERROR 0x00002000
+#define BM_GPMI_STAT_DEV4_ERROR 0x00001000
+#define BM_GPMI_STAT_DEV3_ERROR 0x00000800
+#define BM_GPMI_STAT_DEV2_ERROR 0x00000400
+#define BM_GPMI_STAT_DEV1_ERROR 0x00000200
+#define BM_GPMI_STAT_DEV0_ERROR 0x00000100
+#define BP_GPMI_STAT_RSVD1 5
+#define BM_GPMI_STAT_RSVD1 0x000000E0
+#define BF_GPMI_STAT_RSVD1(v) \
+ (((v) << 5) & BM_GPMI_STAT_RSVD1)
+#define BM_GPMI_STAT_ATA_IRQ 0x00000010
+#define BM_GPMI_STAT_INVALID_BUFFER_MASK 0x00000008
+#define BM_GPMI_STAT_FIFO_EMPTY 0x00000004
+#define BV_GPMI_STAT_FIFO_EMPTY__NOT_EMPTY 0x0
+#define BV_GPMI_STAT_FIFO_EMPTY__EMPTY 0x1
+#define BM_GPMI_STAT_FIFO_FULL 0x00000002
+#define BV_GPMI_STAT_FIFO_FULL__NOT_FULL 0x0
+#define BV_GPMI_STAT_FIFO_FULL__FULL 0x1
+#define BM_GPMI_STAT_PRESENT 0x00000001
+#define BV_GPMI_STAT_PRESENT__UNAVAILABLE 0x0
+#define BV_GPMI_STAT_PRESENT__AVAILABLE 0x1
+
+#define HW_GPMI_DEBUG (0x000000c0)
+
+#define BP_GPMI_DEBUG_WAIT_FOR_READY_END 24
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END 0xFF000000
+#define BF_GPMI_DEBUG_WAIT_FOR_READY_END(v) \
+ (((v) << 24) & BM_GPMI_DEBUG_WAIT_FOR_READY_END)
+#define BP_GPMI_DEBUG_DMA_SENSE 16
+#define BM_GPMI_DEBUG_DMA_SENSE 0x00FF0000
+#define BF_GPMI_DEBUG_DMA_SENSE(v) \
+ (((v) << 16) & BM_GPMI_DEBUG_DMA_SENSE)
+#define BP_GPMI_DEBUG_DMAREQ 8
+#define BM_GPMI_DEBUG_DMAREQ 0x0000FF00
+#define BF_GPMI_DEBUG_DMAREQ(v) \
+ (((v) << 8) & BM_GPMI_DEBUG_DMAREQ)
+#define BP_GPMI_DEBUG_CMD_END 0
+#define BM_GPMI_DEBUG_CMD_END 0x000000FF
+#define BF_GPMI_DEBUG_CMD_END(v) \
+ (((v) << 0) & BM_GPMI_DEBUG_CMD_END)
+
+#define HW_GPMI_VERSION (0x000000d0)
+
+#define BP_GPMI_VERSION_MAJOR 24
+#define BM_GPMI_VERSION_MAJOR 0xFF000000
+#define BF_GPMI_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_GPMI_VERSION_MAJOR)
+#define BP_GPMI_VERSION_MINOR 16
+#define BM_GPMI_VERSION_MINOR 0x00FF0000
+#define BF_GPMI_VERSION_MINOR(v) \
+ (((v) << 16) & BM_GPMI_VERSION_MINOR)
+#define BP_GPMI_VERSION_STEP 0
+#define BM_GPMI_VERSION_STEP 0x0000FFFF
+#define BF_GPMI_VERSION_STEP(v) \
+ (((v) << 0) & BM_GPMI_VERSION_STEP)
+
+#define HW_GPMI_DEBUG2 (0x000000e0)
+
+#define BP_GPMI_DEBUG2_RSVD1 28
+#define BM_GPMI_DEBUG2_RSVD1 0xF0000000
+#define BF_GPMI_DEBUG2_RSVD1(v) \
+ (((v) << 28) & BM_GPMI_DEBUG2_RSVD1)
+#define BP_GPMI_DEBUG2_UDMA_STATE 24
+#define BM_GPMI_DEBUG2_UDMA_STATE 0x0F000000
+#define BF_GPMI_DEBUG2_UDMA_STATE(v) \
+ (((v) << 24) & BM_GPMI_DEBUG2_UDMA_STATE)
+#define BM_GPMI_DEBUG2_BUSY 0x00800000
+#define BV_GPMI_DEBUG2_BUSY__DISABLED 0x0
+#define BV_GPMI_DEBUG2_BUSY__ENABLED 0x1
+#define BP_GPMI_DEBUG2_PIN_STATE 20
+#define BM_GPMI_DEBUG2_PIN_STATE 0x00700000
+#define BF_GPMI_DEBUG2_PIN_STATE(v) \
+ (((v) << 20) & BM_GPMI_DEBUG2_PIN_STATE)
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_IDLE 0x0
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_BYTCNT 0x1
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_ADDR 0x2
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_STALL 0x3
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_STROBE 0x4
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_ATARDY 0x5
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_DHOLD 0x6
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_DONE 0x7
+#define BP_GPMI_DEBUG2_MAIN_STATE 16
+#define BM_GPMI_DEBUG2_MAIN_STATE 0x000F0000
+#define BF_GPMI_DEBUG2_MAIN_STATE(v) \
+ (((v) << 16) & BM_GPMI_DEBUG2_MAIN_STATE)
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_IDLE 0x0
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_BYTCNT 0x1
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFE 0x2
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFR 0x3
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DMAREQ 0x4
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DMAACK 0x5
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFF 0x6
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_LDFIFO 0x7
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_LDDMAR 0x8
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_RDCMP 0x9
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DONE 0xA
+#define BP_GPMI_DEBUG2_SYND2GPMI_BE 12
+#define BM_GPMI_DEBUG2_SYND2GPMI_BE 0x0000F000
+#define BF_GPMI_DEBUG2_SYND2GPMI_BE(v) \
+ (((v) << 12) & BM_GPMI_DEBUG2_SYND2GPMI_BE)
+#define BM_GPMI_DEBUG2_GPMI2SYND_VALID 0x00000800
+#define BM_GPMI_DEBUG2_GPMI2SYND_READY 0x00000400
+#define BM_GPMI_DEBUG2_SYND2GPMI_VALID 0x00000200
+#define BM_GPMI_DEBUG2_SYND2GPMI_READY 0x00000100
+#define BM_GPMI_DEBUG2_VIEW_DELAYED_RDN 0x00000080
+#define BM_GPMI_DEBUG2_UPDATE_WINDOW 0x00000040
+#define BP_GPMI_DEBUG2_RDN_TAP 0
+#define BM_GPMI_DEBUG2_RDN_TAP 0x0000003F
+#define BF_GPMI_DEBUG2_RDN_TAP(v) \
+ (((v) << 0) & BM_GPMI_DEBUG2_RDN_TAP)
+
+#define HW_GPMI_DEBUG3 (0x000000f0)
+
+#define BP_GPMI_DEBUG3_APB_WORD_CNTR 16
+#define BM_GPMI_DEBUG3_APB_WORD_CNTR 0xFFFF0000
+#define BF_GPMI_DEBUG3_APB_WORD_CNTR(v) \
+ (((v) << 16) & BM_GPMI_DEBUG3_APB_WORD_CNTR)
+#define BP_GPMI_DEBUG3_DEV_WORD_CNTR 0
+#define BM_GPMI_DEBUG3_DEV_WORD_CNTR 0x0000FFFF
+#define BF_GPMI_DEBUG3_DEV_WORD_CNTR(v) \
+ (((v) << 0) & BM_GPMI_DEBUG3_DEV_WORD_CNTR)
+
+#define HW_GPMI_READ_DDR_DLL_CTRL (0x00000100)
+
+#define BP_GPMI_READ_DDR_DLL_CTRL_REF_UPDATE_INT 28
+#define BM_GPMI_READ_DDR_DLL_CTRL_REF_UPDATE_INT 0xF0000000
+#define BF_GPMI_READ_DDR_DLL_CTRL_REF_UPDATE_INT(v) \
+ (((v) << 28) & BM_GPMI_READ_DDR_DLL_CTRL_REF_UPDATE_INT)
+#define BP_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT 20
+#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT 0x0FF00000
+#define BF_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT(v) \
+ (((v) << 20) & BM_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT)
+#define BP_GPMI_READ_DDR_DLL_CTRL_RSVD1 18
+#define BM_GPMI_READ_DDR_DLL_CTRL_RSVD1 0x000C0000
+#define BF_GPMI_READ_DDR_DLL_CTRL_RSVD1(v) \
+ (((v) << 18) & BM_GPMI_READ_DDR_DLL_CTRL_RSVD1)
+#define BP_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE_VAL 10
+#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE_VAL 0x0003FC00
+#define BF_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE_VAL(v) \
+ (((v) << 10) & BM_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE_VAL)
+#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_OVERRIDE 0x00000200
+#define BM_GPMI_READ_DDR_DLL_CTRL_REFCLK_ON 0x00000100
+#define BM_GPMI_READ_DDR_DLL_CTRL_GATE_UPDATE 0x00000080
+#define BP_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET 3
+#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET 0x00000078
+#define BF_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET(v) \
+ (((v) << 3) & BM_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET)
+#define BM_GPMI_READ_DDR_DLL_CTRL_SLV_FORCE_UPD 0x00000004
+#define BM_GPMI_READ_DDR_DLL_CTRL_RESET 0x00000002
+#define BM_GPMI_READ_DDR_DLL_CTRL_ENABLE 0x00000001
+
+#define HW_GPMI_WRITE_DDR_DLL_CTRL (0x00000110)
+
+#define BP_GPMI_WRITE_DDR_DLL_CTRL_REF_UPDATE_INT 28
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_REF_UPDATE_INT 0xF0000000
+#define BF_GPMI_WRITE_DDR_DLL_CTRL_REF_UPDATE_INT(v) \
+ (((v) << 28) & BM_GPMI_WRITE_DDR_DLL_CTRL_REF_UPDATE_INT)
+#define BP_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT 20
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT 0x0FF00000
+#define BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT(v) \
+ (((v) << 20) & BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT)
+#define BP_GPMI_WRITE_DDR_DLL_CTRL_RSVD1 18
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_RSVD1 0x000C0000
+#define BF_GPMI_WRITE_DDR_DLL_CTRL_RSVD1(v) \
+ (((v) << 18) & BM_GPMI_WRITE_DDR_DLL_CTRL_RSVD1)
+#define BP_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE_VAL 10
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE_VAL 0x0003FC00
+#define BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE_VAL(v) \
+ (((v) << 10) & BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE_VAL)
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_OVERRIDE 0x00000200
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_REFCLK_ON 0x00000100
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_GATE_UPDATE 0x00000080
+#define BP_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET 3
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET 0x00000078
+#define BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET(v) \
+ (((v) << 3) & BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET)
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_FORCE_UPD 0x00000004
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_RESET 0x00000002
+#define BM_GPMI_WRITE_DDR_DLL_CTRL_ENABLE 0x00000001
+
+#define HW_GPMI_READ_DDR_DLL_STS (0x00000120)
+
+#define BP_GPMI_READ_DDR_DLL_STS_RSVD1 25
+#define BM_GPMI_READ_DDR_DLL_STS_RSVD1 0xFE000000
+#define BF_GPMI_READ_DDR_DLL_STS_RSVD1(v) \
+ (((v) << 25) & BM_GPMI_READ_DDR_DLL_STS_RSVD1)
+#define BP_GPMI_READ_DDR_DLL_STS_REF_SEL 17
+#define BM_GPMI_READ_DDR_DLL_STS_REF_SEL 0x01FE0000
+#define BF_GPMI_READ_DDR_DLL_STS_REF_SEL(v) \
+ (((v) << 17) & BM_GPMI_READ_DDR_DLL_STS_REF_SEL)
+#define BM_GPMI_READ_DDR_DLL_STS_REF_LOCK 0x00010000
+#define BP_GPMI_READ_DDR_DLL_STS_RSVD0 9
+#define BM_GPMI_READ_DDR_DLL_STS_RSVD0 0x0000FE00
+#define BF_GPMI_READ_DDR_DLL_STS_RSVD0(v) \
+ (((v) << 9) & BM_GPMI_READ_DDR_DLL_STS_RSVD0)
+#define BP_GPMI_READ_DDR_DLL_STS_SLV_SEL 1
+#define BM_GPMI_READ_DDR_DLL_STS_SLV_SEL 0x000001FE
+#define BF_GPMI_READ_DDR_DLL_STS_SLV_SEL(v) \
+ (((v) << 1) & BM_GPMI_READ_DDR_DLL_STS_SLV_SEL)
+#define BM_GPMI_READ_DDR_DLL_STS_SLV_LOCK 0x00000001
+
+#define HW_GPMI_WRITE_DDR_DLL_STS (0x00000130)
+
+#define BP_GPMI_WRITE_DDR_DLL_STS_RSVD1 25
+#define BM_GPMI_WRITE_DDR_DLL_STS_RSVD1 0xFE000000
+#define BF_GPMI_WRITE_DDR_DLL_STS_RSVD1(v) \
+ (((v) << 25) & BM_GPMI_WRITE_DDR_DLL_STS_RSVD1)
+#define BP_GPMI_WRITE_DDR_DLL_STS_REF_SEL 17
+#define BM_GPMI_WRITE_DDR_DLL_STS_REF_SEL 0x01FE0000
+#define BF_GPMI_WRITE_DDR_DLL_STS_REF_SEL(v) \
+ (((v) << 17) & BM_GPMI_WRITE_DDR_DLL_STS_REF_SEL)
+#define BM_GPMI_WRITE_DDR_DLL_STS_REF_LOCK 0x00010000
+#define BP_GPMI_WRITE_DDR_DLL_STS_RSVD0 9
+#define BM_GPMI_WRITE_DDR_DLL_STS_RSVD0 0x0000FE00
+#define BF_GPMI_WRITE_DDR_DLL_STS_RSVD0(v) \
+ (((v) << 9) & BM_GPMI_WRITE_DDR_DLL_STS_RSVD0)
+#define BP_GPMI_WRITE_DDR_DLL_STS_SLV_SEL 1
+#define BM_GPMI_WRITE_DDR_DLL_STS_SLV_SEL 0x000001FE
+#define BF_GPMI_WRITE_DDR_DLL_STS_SLV_SEL(v) \
+ (((v) << 1) & BM_GPMI_WRITE_DDR_DLL_STS_SLV_SEL)
+#define BM_GPMI_WRITE_DDR_DLL_STS_SLV_LOCK 0x00000001
+#endif /* __ARCH_ARM___GPMI_H */
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c
new file mode 100644
index 000000000000..e90a934b5c10
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-common.c
@@ -0,0 +1,898 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "gpmi-nfc.h"
+
+/**
+ * gpmi_nfc_bch_isr - BCH interrupt service routine.
+ *
+ * @interrupt_number: The interrupt number.
+ * @cookie: A cookie that contains a pointer to the owning device
+ * data structure.
+ */
+irqreturn_t gpmi_nfc_bch_isr(int irq, void *cookie)
+{
+ struct gpmi_nfc_data *this = cookie;
+ struct nfc_hal *nfc = this->nfc;
+
+ /* Clear the interrupt. */
+ nfc->clear_bch(this);
+
+ /* Release the base level. */
+ complete(&(nfc->bch_done));
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * gpmi_nfc_dma_isr - DMA interrupt service routine.
+ *
+ * @interrupt_number: The interrupt number.
+ * @cookie: A cookie that contains a pointer to the owning device
+ * data structure.
+ */
+irqreturn_t gpmi_nfc_dma_isr(int irq, void *cookie)
+{
+ struct gpmi_nfc_data *this = cookie;
+ struct nfc_hal *nfc = this->nfc;
+
+ /* Acknowledge the DMA channel's interrupt. */
+ mxs_dma_ack_irq(nfc->isr_dma_channel);
+
+ /* Release the base level. */
+ complete(&(nfc->dma_done));
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * gpmi_nfc_dma_init() - Initializes DMA.
+ *
+ * @this: Per-device data.
+ */
+int gpmi_nfc_dma_init(struct gpmi_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct nfc_hal *nfc = this->nfc;
+ int i;
+ int error;
+
+ /* Allocate the DMA descriptors. */
+ for (i = 0; i < NFC_DMA_DESCRIPTOR_COUNT; i++) {
+ nfc->dma_descriptors[i] = mxs_dma_alloc_desc();
+ if (!nfc->dma_descriptors[i]) {
+ dev_err(dev, "Cannot allocate all DMA descriptors.\n");
+ error = -ENOMEM;
+ goto exit_descriptor_allocation;
+ }
+ }
+ return 0;
+
+exit_descriptor_allocation:
+ while (--i >= 0)
+ mxs_dma_free_desc(this->nfc->dma_descriptors[i]);
+ return error;
+}
+
+/**
+ * gpmi_nfc_dma_exit() - Shuts down DMA.
+ *
+ * @this: Per-device data.
+ */
+void gpmi_nfc_dma_exit(struct gpmi_nfc_data *this)
+{
+ struct nfc_hal *nfc = this->nfc;
+ int i;
+
+ /* Free the DMA descriptors. */
+ for (i = 0; i < NFC_DMA_DESCRIPTOR_COUNT; i++)
+ mxs_dma_free_desc(nfc->dma_descriptors[i]);
+}
+
+/**
+ * gpmi_nfc_set_geometry() - Shared NFC geometry configuration.
+ *
+ * In principle, computing the NFC geometry is version-specific. However, at
+ * this writing all, versions share the same page model, so this code can also
+ * be shared.
+ *
+ * @this: Per-device data.
+ */
+int gpmi_nfc_set_geometry(struct gpmi_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct nfc_geometry *geometry = &this->nfc_geometry;
+ struct boot_rom_helper *rom = this->rom;
+ struct mtd_info *mtd = &this->mil.mtd;
+ unsigned int metadata_size;
+ unsigned int status_size;
+ unsigned int chunk_data_size_in_bits;
+ unsigned int chunk_ecc_size_in_bits;
+ unsigned int chunk_total_size_in_bits;
+ unsigned int block_mark_chunk_number;
+ unsigned int block_mark_chunk_bit_offset;
+ unsigned int block_mark_bit_offset;
+
+ /* At this writing, we support only BCH. */
+ geometry->ecc_algorithm = "BCH";
+
+ /*
+ * We always choose a metadata size of 10. Don't try to make sense of
+ * it -- this is really only for historical compatibility.
+ */
+ geometry->metadata_size_in_bytes = 10;
+
+ /* ECC chunks */
+ geometry->ecc_chunk_size_in_bytes = 512;
+ if (is_ddr_nand(&this->device_info))
+ geometry->ecc_chunk_size_in_bytes = 1024;
+
+ /* Compute the page size, include page and oob. */
+ geometry->page_size_in_bytes = mtd->writesize + mtd->oobsize;
+
+ /*
+ * Compute the total number of ECC chunks in a page. This includes the
+ * slightly larger chunk at the beginning of the page, which contains
+ * both data and metadata.
+ */
+ geometry->ecc_chunk_count = mtd->writesize /
+ geometry->ecc_chunk_size_in_bytes;
+
+ /*
+ * We use the same ECC strength for all chunks, including the first one.
+ * At this writing, we base our ECC strength choice entirely on the
+ * the physical page geometry. In the future, this should be changed to
+ * pay attention to the detailed device information we gathered earlier.
+ */
+ geometry->ecc_strength = 0;
+
+ switch (mtd->writesize) {
+ case 2048:
+ geometry->ecc_strength = 8;
+ break;
+ case 4096:
+ switch (mtd->oobsize) {
+ case 128:
+ geometry->ecc_strength = 8;
+ break;
+ case 218:
+ geometry->ecc_strength = 16;
+ break;
+ }
+ break;
+ case 8192:
+ geometry->ecc_strength = 24;
+ /*
+ * ONFI/TOGGLE nand needs GF14, so re-culculate DMA page size.
+ * The ONFI nand must do the reculation,
+ * else it will fail in DMA.
+ */
+ if (is_ddr_nand(&this->device_info))
+ geometry->page_size_in_bytes =
+ mtd->writesize
+ + geometry->metadata_size_in_bytes +
+ (geometry->ecc_strength * 14 * 8 /
+ geometry->ecc_chunk_count);
+ break;
+ }
+
+ if (!geometry->ecc_strength) {
+ dev_err(dev, "Unsupported page geometry.\n");
+ return !0;
+ }
+
+ /*
+ * The payload buffer contains the data area of a page. The ECC engine
+ * only needs what's required to hold the data.
+ */
+ geometry->payload_size_in_bytes = mtd->writesize;
+
+ /*
+ * In principle, computing the auxiliary buffer geometry is NFC
+ * version-specific. However, at this writing, all versions share the
+ * same model, so this code can also be shared.
+ *
+ * The auxiliary buffer contains the metadata and the ECC status. The
+ * metadata is padded to the nearest 32-bit boundary. The ECC status
+ * contains one byte for every ECC chunk, and is also padded to the
+ * nearest 32-bit boundary.
+ */
+ metadata_size = (geometry->metadata_size_in_bytes + 0x3) & ~0x3;
+ status_size = (geometry->ecc_chunk_count + 0x3) & ~0x3;
+
+ geometry->auxiliary_size_in_bytes = metadata_size + status_size;
+ geometry->auxiliary_status_offset = metadata_size;
+
+ /* Check if we're going to do block mark swapping. */
+ if (!rom->swap_block_mark)
+ return 0;
+
+ /*
+ * If control arrives here, we're doing block mark swapping, so we need
+ * to compute the byte and bit offsets of the physical block mark within
+ * the ECC-based view of the page data. In principle, this isn't a
+ * difficult computation -- but it's very important and it's easy to get
+ * it wrong, so we do it carefully.
+ *
+ * Note that this calculation is simpler because we use the same ECC
+ * strength for all chunks, including the zero'th one, which contains
+ * the metadata. The calculation would be slightly more complicated
+ * otherwise.
+ *
+ * We start by computing the physical bit offset of the block mark. We
+ * then subtract the number of metadata and ECC bits appearing before
+ * the mark to arrive at its bit offset within the data alone.
+ */
+
+ /* Compute some important facts about chunk geometry. */
+ chunk_data_size_in_bits = geometry->ecc_chunk_size_in_bytes * 8;
+
+ /* ONFI nand needs GF14 */
+ if (is_ddr_nand(&this->device_info))
+ chunk_ecc_size_in_bits = geometry->ecc_strength * 14;
+ else
+ chunk_ecc_size_in_bits = geometry->ecc_strength * 13;
+
+ chunk_total_size_in_bits =
+ chunk_data_size_in_bits + chunk_ecc_size_in_bits;
+
+ /* Compute the bit offset of the block mark within the physical page. */
+ block_mark_bit_offset = mtd->writesize * 8;
+
+ /* Subtract the metadata bits. */
+ block_mark_bit_offset -= geometry->metadata_size_in_bytes * 8;
+
+ /*
+ * Compute the chunk number (starting at zero) in which the block mark
+ * appears.
+ */
+ block_mark_chunk_number =
+ block_mark_bit_offset / chunk_total_size_in_bits;
+
+ /*
+ * Compute the bit offset of the block mark within its chunk, and
+ * validate it.
+ */
+ block_mark_chunk_bit_offset =
+ block_mark_bit_offset -
+ (block_mark_chunk_number * chunk_total_size_in_bits);
+
+ if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) {
+ /*
+ * If control arrives here, the block mark actually appears in
+ * the ECC bits of this chunk. This wont' work.
+ */
+ dev_err(dev, "Unsupported page geometry "
+ "(block mark in ECC): %u:%u\n",
+ mtd->writesize, mtd->oobsize);
+ return !0;
+ }
+
+ /*
+ * Now that we know the chunk number in which the block mark appears,
+ * we can subtract all the ECC bits that appear before it.
+ */
+ block_mark_bit_offset -=
+ block_mark_chunk_number * chunk_ecc_size_in_bits;
+
+ /*
+ * We now know the absolute bit offset of the block mark within the
+ * ECC-based data. We can now compute the byte offset and the bit
+ * offset within the byte.
+ */
+ geometry->block_mark_byte_offset = block_mark_bit_offset / 8;
+ geometry->block_mark_bit_offset = block_mark_bit_offset % 8;
+
+ return 0;
+}
+
+/**
+ * gpmi_nfc_dma_go - Run a DMA channel.
+ *
+ * @this: Per-device data structure.
+ * @dma_channel: The DMA channel we're going to use.
+ */
+int gpmi_nfc_dma_go(struct gpmi_nfc_data *this, int dma_channel)
+{
+ struct device *dev = this->dev;
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ unsigned long timeout;
+ int error;
+ LIST_HEAD(tmp_desc_list);
+
+ /* Get ready... */
+ nfc->isr_dma_channel = dma_channel;
+ init_completion(&nfc->dma_done);
+ mxs_dma_enable_irq(dma_channel, 1);
+
+ /* Go! */
+ mxs_dma_enable(dma_channel);
+
+ /* Wait for it to finish. */
+ timeout = wait_for_completion_timeout(&nfc->dma_done,
+ msecs_to_jiffies(1000));
+ error = (!timeout) ? -ETIMEDOUT : 0;
+ if (error) {
+ struct mxs_dma_info info;
+
+ mxs_dma_get_info(dma_channel, &info);
+ dev_err(dev, "[%s] Chip: %u, DMA Channel: %d, Error %d\n",
+ __func__, dma_channel - resources->dma_low_channel,
+ dma_channel, error);
+ } else {
+ /* Clear out the descriptors we just ran. */
+ mxs_dma_cooked(dma_channel, &tmp_desc_list);
+ }
+
+ /* Shut the DMA channel down. */
+ mxs_dma_reset(dma_channel);
+ mxs_dma_enable_irq(dma_channel, 0);
+ mxs_dma_disable(dma_channel);
+ return error;
+}
+
+/*
+ * This function is used in BCH reading or BCH writing.
+ * It will wait for the BCH interrupt as long as one second.
+ * Actually, we will wait for two interrupts, the DMA interrupt and
+ * BCH interrupt.
+ *
+ * @this: Per-device data structure.
+ * @dma_channel: DMA channel
+ *
+ */
+int start_dma_with_bch_irq(struct gpmi_nfc_data *this, int dma_channel)
+{
+ struct nfc_hal *nfc = this->nfc;
+ int error = 0;
+
+ /* Prepare to receive an interrupt from the BCH block. */
+ init_completion(&nfc->bch_done);
+
+ /* Go! */
+ error = gpmi_nfc_dma_go(this, dma_channel);
+ if (error)
+ printk(KERN_ERR "[ %s ] DMA error\n", __func__);
+
+ /* Wait for the interrupt from the BCH block. */
+ error = wait_for_completion_timeout(&nfc->bch_done,
+ msecs_to_jiffies(1000));
+ error = (!error) ? -ETIMEDOUT : 0;
+ if (error)
+ printk(KERN_ERR "[ %s ] bch timeout!!!\n", __func__);
+ return error;
+}
+
+/* This function is called in non-BCH DMA operations */
+int start_dma_without_bch_irq(struct gpmi_nfc_data *this, int dma_channel)
+{
+ int error = 0;
+
+ /* Go! */
+ error = gpmi_nfc_dma_go(this, dma_channel);
+ if (error)
+ printk(KERN_ERR "[ %s ] DMA error\n", __func__);
+ return error;
+}
+
+/**
+ * ns_to_cycles - Converts time in nanoseconds to cycles.
+ *
+ * @ntime: The time, in nanoseconds.
+ * @period: The cycle period, in nanoseconds.
+ * @min: The minimum allowable number of cycles.
+ */
+static unsigned int ns_to_cycles(unsigned int time,
+ unsigned int period, unsigned int min)
+{
+ unsigned int k;
+
+ /*
+ * Compute the minimum number of cycles that entirely contain the
+ * given time.
+ */
+ k = (time + period - 1) / period;
+ return max(k, min);
+}
+
+/**
+ * gpmi_compute_hardware_timing - Apply timing to current hardware conditions.
+ *
+ * @this: Per-device data.
+ * @hardware_timing: A pointer to a hardware timing structure that will receive
+ * the results of our calculations.
+ */
+int gpmi_nfc_compute_hardware_timing(struct gpmi_nfc_data *this,
+ struct gpmi_nfc_hardware_timing *hw)
+{
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct nfc_hal *nfc = this->nfc;
+ struct nand_chip *nand = &this->mil.nand;
+ struct gpmi_nfc_timing target = nfc->timing;
+ bool improved_timing_is_available;
+ unsigned long clock_frequency_in_hz;
+ unsigned int clock_period_in_ns;
+ bool dll_use_half_periods;
+ unsigned int dll_delay_shift;
+ unsigned int max_sample_delay_in_ns;
+ unsigned int address_setup_in_cycles;
+ unsigned int data_setup_in_ns;
+ unsigned int data_setup_in_cycles;
+ unsigned int data_hold_in_cycles;
+ int ideal_sample_delay_in_ns;
+ unsigned int sample_delay_factor;
+ int tEYE;
+ unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns;
+ unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns;
+
+ /*
+ * If there are multiple chips, we need to relax the timings to allow
+ * for signal distortion due to higher capacitance.
+ */
+ if (nand->numchips > 2) {
+ target.data_setup_in_ns += 10;
+ target.data_hold_in_ns += 10;
+ target.address_setup_in_ns += 10;
+ } else if (nand->numchips > 1) {
+ target.data_setup_in_ns += 5;
+ target.data_hold_in_ns += 5;
+ target.address_setup_in_ns += 5;
+ }
+
+ /* Check if improved timing information is available. */
+ improved_timing_is_available =
+ (target.tREA_in_ns >= 0) &&
+ (target.tRLOH_in_ns >= 0) &&
+ (target.tRHOH_in_ns >= 0) ;
+
+ /* Inspect the clock. */
+ clock_frequency_in_hz = nfc->clock_frequency_in_hz;
+ clock_period_in_ns = 1000000000 / clock_frequency_in_hz;
+
+ /*
+ * The NFC quantizes setup and hold parameters in terms of clock cycles.
+ * Here, we quantize the setup and hold timing parameters to the
+ * next-highest clock period to make sure we apply at least the
+ * specified times.
+ *
+ * For data setup and data hold, the hardware interprets a value of zero
+ * as the largest possible delay. This is not what's intended by a zero
+ * in the input parameter, so we impose a minimum of one cycle.
+ */
+ data_setup_in_cycles = ns_to_cycles(target.data_setup_in_ns,
+ clock_period_in_ns, 1);
+ data_hold_in_cycles = ns_to_cycles(target.data_hold_in_ns,
+ clock_period_in_ns, 1);
+ address_setup_in_cycles = ns_to_cycles(target.address_setup_in_ns,
+ clock_period_in_ns, 0);
+
+ /*
+ * The clock's period affects the sample delay in a number of ways:
+ *
+ * (1) The NFC HAL tells us the maximum clock period the sample delay
+ * DLL can tolerate. If the clock period is greater than half that
+ * maximum, we must configure the DLL to be driven by half periods.
+ *
+ * (2) We need to convert from an ideal sample delay, in ns, to a
+ * "sample delay factor," which the NFC uses. This factor depends on
+ * whether we're driving the DLL with full or half periods.
+ * Paraphrasing the reference manual:
+ *
+ * AD = SDF x 0.125 x RP
+ *
+ * where:
+ *
+ * AD is the applied delay, in ns.
+ * SDF is the sample delay factor, which is dimensionless.
+ * RP is the reference period, in ns, which is a full clock period
+ * if the DLL is being driven by full periods, or half that if
+ * the DLL is being driven by half periods.
+ *
+ * Let's re-arrange this in a way that's more useful to us:
+ *
+ * 8
+ * SDF = AD x ----
+ * RP
+ *
+ * The reference period is either the clock period or half that, so this
+ * is:
+ *
+ * 8 AD x DDF
+ * SDF = AD x ----- = --------
+ * f x P P
+ *
+ * where:
+ *
+ * f is 1 or 1/2, depending on how we're driving the DLL.
+ * P is the clock period.
+ * DDF is the DLL Delay Factor, a dimensionless value that
+ * incorporates all the constants in the conversion.
+ *
+ * DDF will be either 8 or 16, both of which are powers of two. We can
+ * reduce the cost of this conversion by using bit shifts instead of
+ * multiplication or division. Thus:
+ *
+ * AD << DDS
+ * SDF = ---------
+ * P
+ *
+ * or
+ *
+ * AD = (SDF >> DDS) x P
+ *
+ * where:
+ *
+ * DDS is the DLL Delay Shift, the logarithm to base 2 of the DDF.
+ */
+ if (clock_period_in_ns > (nfc->max_dll_clock_period_in_ns >> 1)) {
+ dll_use_half_periods = true;
+ dll_delay_shift = 3 + 1;
+ } else {
+ dll_use_half_periods = false;
+ dll_delay_shift = 3;
+ }
+
+ /*
+ * Compute the maximum sample delay the NFC allows, under current
+ * conditions. If the clock is running too slowly, no sample delay is
+ * possible.
+ */
+ if (clock_period_in_ns > nfc->max_dll_clock_period_in_ns)
+ max_sample_delay_in_ns = 0;
+ else {
+
+ /*
+ * Compute the delay implied by the largest sample delay factor
+ * the NFC allows.
+ */
+
+ max_sample_delay_in_ns =
+ (nfc->max_sample_delay_factor * clock_period_in_ns) >>
+ dll_delay_shift;
+
+ /*
+ * Check if the implied sample delay larger than the NFC
+ * actually allows.
+ */
+
+ if (max_sample_delay_in_ns > nfc->max_dll_delay_in_ns)
+ max_sample_delay_in_ns = nfc->max_dll_delay_in_ns;
+
+ }
+
+ /*
+ * Check if improved timing information is available. If not, we have to
+ * use a less-sophisticated algorithm.
+ */
+
+ if (!improved_timing_is_available) {
+
+ /*
+ * Fold the read setup time required by the NFC into the ideal
+ * sample delay.
+ */
+
+ ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns +
+ nfc->internal_data_setup_in_ns;
+
+ /*
+ * The ideal sample delay may be greater than the maximum
+ * allowed by the NFC. If so, we can trade off sample delay time
+ * for more data setup time.
+ *
+ * In each iteration of the following loop, we add a cycle to
+ * the data setup time and subtract a corresponding amount from
+ * the sample delay until we've satisified the constraints or
+ * can't do any better.
+ */
+
+ while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
+ (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
+
+ data_setup_in_cycles++;
+ ideal_sample_delay_in_ns -= clock_period_in_ns;
+
+ if (ideal_sample_delay_in_ns < 0)
+ ideal_sample_delay_in_ns = 0;
+
+ }
+
+ /*
+ * Compute the sample delay factor that corresponds most closely
+ * to the ideal sample delay. If the result is too large for the
+ * NFC, use the maximum value.
+ *
+ * Notice that we use the ns_to_cycles function to compute the
+ * sample delay factor. We do this because the form of the
+ * computation is the same as that for calculating cycles.
+ */
+
+ sample_delay_factor =
+ ns_to_cycles(
+ ideal_sample_delay_in_ns << dll_delay_shift,
+ clock_period_in_ns, 0);
+
+ if (sample_delay_factor > nfc->max_sample_delay_factor)
+ sample_delay_factor = nfc->max_sample_delay_factor;
+
+ /* Skip to the part where we return our results. */
+
+ goto return_results;
+
+ }
+
+ /*
+ * If control arrives here, we have more detailed timing information,
+ * so we can use a better algorithm.
+ */
+
+ /*
+ * Fold the read setup time required by the NFC into the maximum
+ * propagation delay.
+ */
+
+ max_prop_delay_in_ns += nfc->internal_data_setup_in_ns;
+
+ /*
+ * Earlier, we computed the number of clock cycles required to satisfy
+ * the data setup time. Now, we need to know the actual nanoseconds.
+ */
+
+ data_setup_in_ns = clock_period_in_ns * data_setup_in_cycles;
+
+ /*
+ * Compute tEYE, the width of the data eye when reading from the NAND
+ * Flash. The eye width is fundamentally determined by the data setup
+ * time, perturbed by propagation delays and some characteristics of the
+ * NAND Flash device.
+ *
+ * start of the eye = max_prop_delay + tREA
+ * end of the eye = min_prop_delay + tRHOH + data_setup
+ */
+
+ tEYE = (int)min_prop_delay_in_ns + (int)target.tRHOH_in_ns +
+ (int)data_setup_in_ns;
+
+ tEYE -= (int)max_prop_delay_in_ns + (int)target.tREA_in_ns;
+
+ /*
+ * The eye must be open. If it's not, we can try to open it by
+ * increasing its main forcer, the data setup time.
+ *
+ * In each iteration of the following loop, we increase the data setup
+ * time by a single clock cycle. We do this until either the eye is
+ * open or we run into NFC limits.
+ */
+
+ while ((tEYE <= 0) &&
+ (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
+ /* Give a cycle to data setup. */
+ data_setup_in_cycles++;
+ /* Synchronize the data setup time with the cycles. */
+ data_setup_in_ns += clock_period_in_ns;
+ /* Adjust tEYE accordingly. */
+ tEYE += clock_period_in_ns;
+ }
+
+ /*
+ * When control arrives here, the eye is open. The ideal time to sample
+ * the data is in the center of the eye:
+ *
+ * end of the eye + start of the eye
+ * --------------------------------- - data_setup
+ * 2
+ *
+ * After some algebra, this simplifies to the code immediately below.
+ */
+
+ ideal_sample_delay_in_ns =
+ ((int)max_prop_delay_in_ns +
+ (int)target.tREA_in_ns +
+ (int)min_prop_delay_in_ns +
+ (int)target.tRHOH_in_ns -
+ (int)data_setup_in_ns) >> 1;
+
+ /*
+ * The following figure illustrates some aspects of a NAND Flash read:
+ *
+ *
+ * __ _____________________________________
+ * RDN \_________________/
+ *
+ * <---- tEYE ----->
+ * /-----------------\
+ * Read Data ----------------------------< >---------
+ * \-----------------/
+ * ^ ^ ^ ^
+ * | | | |
+ * |<--Data Setup -->|<--Delay Time -->| |
+ * | | | |
+ * | | |
+ * | |<-- Quantized Delay Time -->|
+ * | | |
+ *
+ *
+ * We have some issues we must now address:
+ *
+ * (1) The *ideal* sample delay time must not be negative. If it is, we
+ * jam it to zero.
+ *
+ * (2) The *ideal* sample delay time must not be greater than that
+ * allowed by the NFC. If it is, we can increase the data setup
+ * time, which will reduce the delay between the end of the data
+ * setup and the center of the eye. It will also make the eye
+ * larger, which might help with the next issue...
+ *
+ * (3) The *quantized* sample delay time must not fall either before the
+ * eye opens or after it closes (the latter is the problem
+ * illustrated in the above figure).
+ */
+
+ /* Jam a negative ideal sample delay to zero. */
+
+ if (ideal_sample_delay_in_ns < 0)
+ ideal_sample_delay_in_ns = 0;
+
+ /*
+ * Extend the data setup as needed to reduce the ideal sample delay
+ * below the maximum permitted by the NFC.
+ */
+
+ while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
+ (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
+
+ /* Give a cycle to data setup. */
+ data_setup_in_cycles++;
+ /* Synchronize the data setup time with the cycles. */
+ data_setup_in_ns += clock_period_in_ns;
+ /* Adjust tEYE accordingly. */
+ tEYE += clock_period_in_ns;
+
+ /*
+ * Decrease the ideal sample delay by one half cycle, to keep it
+ * in the middle of the eye.
+ */
+ ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1);
+
+ /* Jam a negative ideal sample delay to zero. */
+ if (ideal_sample_delay_in_ns < 0)
+ ideal_sample_delay_in_ns = 0;
+
+ }
+
+ /*
+ * Compute the sample delay factor that corresponds to the ideal sample
+ * delay. If the result is too large, then use the maximum allowed
+ * value.
+ *
+ * Notice that we use the ns_to_cycles function to compute the sample
+ * delay factor. We do this because the form of the computation is the
+ * same as that for calculating cycles.
+ */
+
+ sample_delay_factor =
+ ns_to_cycles(ideal_sample_delay_in_ns << dll_delay_shift,
+ clock_period_in_ns, 0);
+
+ if (sample_delay_factor > nfc->max_sample_delay_factor)
+ sample_delay_factor = nfc->max_sample_delay_factor;
+
+ /*
+ * These macros conveniently encapsulate a computation we'll use to
+ * continuously evaluate whether or not the data sample delay is inside
+ * the eye.
+ */
+
+ #define IDEAL_DELAY ((int) ideal_sample_delay_in_ns)
+
+ #define QUANTIZED_DELAY \
+ ((int) ((sample_delay_factor * clock_period_in_ns) >> \
+ dll_delay_shift))
+
+ #define DELAY_ERROR (abs(QUANTIZED_DELAY - IDEAL_DELAY))
+
+ #define SAMPLE_IS_NOT_WITHIN_THE_EYE (DELAY_ERROR > (tEYE >> 1))
+
+ /*
+ * While the quantized sample time falls outside the eye, reduce the
+ * sample delay or extend the data setup to move the sampling point back
+ * toward the eye. Do not allow the number of data setup cycles to
+ * exceed the maximum allowed by the NFC.
+ */
+
+ while (SAMPLE_IS_NOT_WITHIN_THE_EYE &&
+ (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
+
+ /*
+ * If control arrives here, the quantized sample delay falls
+ * outside the eye. Check if it's before the eye opens, or after
+ * the eye closes.
+ */
+
+ if (QUANTIZED_DELAY > IDEAL_DELAY) {
+
+ /*
+ * If control arrives here, the quantized sample delay
+ * falls after the eye closes. Decrease the quantized
+ * delay time and then go back to re-evaluate.
+ */
+
+ if (sample_delay_factor != 0)
+ sample_delay_factor--;
+
+ continue;
+
+ }
+
+ /*
+ * If control arrives here, the quantized sample delay falls
+ * before the eye opens. Shift the sample point by increasing
+ * data setup time. This will also make the eye larger.
+ */
+
+ /* Give a cycle to data setup. */
+ data_setup_in_cycles++;
+ /* Synchronize the data setup time with the cycles. */
+ data_setup_in_ns += clock_period_in_ns;
+ /* Adjust tEYE accordingly. */
+ tEYE += clock_period_in_ns;
+
+ /*
+ * Decrease the ideal sample delay by one half cycle, to keep it
+ * in the middle of the eye.
+ */
+ ideal_sample_delay_in_ns -= (clock_period_in_ns >> 1);
+
+ /* ...and one less period for the delay time. */
+ ideal_sample_delay_in_ns -= clock_period_in_ns;
+
+ /* Jam a negative ideal sample delay to zero. */
+ if (ideal_sample_delay_in_ns < 0)
+ ideal_sample_delay_in_ns = 0;
+
+ /*
+ * We have a new ideal sample delay, so re-compute the quantized
+ * delay.
+ */
+
+ sample_delay_factor =
+ ns_to_cycles(
+ ideal_sample_delay_in_ns << dll_delay_shift,
+ clock_period_in_ns, 0);
+
+ if (sample_delay_factor > nfc->max_sample_delay_factor)
+ sample_delay_factor = nfc->max_sample_delay_factor;
+
+ }
+
+ /* Control arrives here when we're ready to return our results. */
+
+return_results:
+ hw->data_setup_in_cycles = data_setup_in_cycles;
+ hw->data_hold_in_cycles = data_hold_in_cycles;
+ hw->address_setup_in_cycles = address_setup_in_cycles;
+ hw->use_half_periods = dll_use_half_periods;
+ hw->sample_delay_factor = sample_delay_factor;
+
+ /* Return success. */
+ return 0;
+}
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c
new file mode 100644
index 000000000000..a1e22a003e53
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v0.c
@@ -0,0 +1,664 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "gpmi-nfc.h"
+
+#include "gpmi-nfc-gpmi-regs-v0.h"
+#include "gpmi-nfc-bch-regs-v0.h"
+
+/**
+ * init() - Initializes the NFC hardware.
+ *
+ * @this: Per-device data.
+ */
+static int init(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ int error;
+
+ /* Initialize DMA. */
+ error = gpmi_nfc_dma_init(this);
+ if (error)
+ return error;
+
+ /* Enable the clock. It will stay on until the end of set_geometry(). */
+ clk_enable(resources->clock);
+
+ /* Reset the GPMI block. */
+ mxs_reset_block(resources->gpmi_regs + HW_GPMI_CTRL0, true);
+
+ /* Choose NAND mode. */
+ __raw_writel(BM_GPMI_CTRL1_GPMI_MODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* Set the IRQ polarity. */
+ __raw_writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Disable write protection. */
+ __raw_writel(BM_GPMI_CTRL1_DEV_RESET,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Select BCH ECC. */
+ __raw_writel(BM_GPMI_CTRL1_BCH_MODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Disable the clock. */
+ clk_disable(resources->clock);
+
+ return 0;
+}
+
+/**
+ * set_geometry() - Configures the NFC geometry.
+ *
+ * @this: Per-device data.
+ */
+static int set_geometry(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_geometry *nfc = &this->nfc_geometry;
+ unsigned int block_count;
+ unsigned int block_size;
+ unsigned int metadata_size;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+
+ /* We make the abstract choices in a common function. */
+ if (gpmi_nfc_set_geometry(this))
+ return !0;
+
+ /* Translate the abstract choices into register fields. */
+ block_count = nfc->ecc_chunk_count - 1;
+ block_size = nfc->ecc_chunk_size_in_bytes;
+ metadata_size = nfc->metadata_size_in_bytes;
+ ecc_strength = nfc->ecc_strength >> 1;
+ page_size = nfc->page_size_in_bytes;
+
+ /* Enable the clock. */
+ clk_enable(resources->clock);
+
+ /*
+ * Reset the BCH block. Notice that we pass in true for the just_enable
+ * flag. This is because the soft reset for the version 0 BCH block
+ * doesn't work. If you try to soft reset the BCH block, it becomes
+ * unusable until the next hard reset.
+ */
+ mxs_reset_block(resources->bch_regs, true);
+
+ /* Configure layout 0. */
+ __raw_writel(
+ BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) |
+ BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) |
+ BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) |
+ BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size) ,
+ resources->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ __raw_writel(
+ BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
+ BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) |
+ BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size) ,
+ resources->bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* Set *all* chip selects to use layout 0. */
+ __raw_writel(0, resources->bch_regs + HW_BCH_LAYOUTSELECT);
+
+ /* Enable interrupts. */
+ __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
+ resources->bch_regs + HW_BCH_CTRL_SET);
+
+ /* Disable the clock. */
+ clk_disable(resources->clock);
+
+ return 0;
+}
+
+/**
+ * set_timing() - Configures the NFC timing.
+ *
+ * @this: Per-device data.
+ * @timing: The timing of interest.
+ */
+static int set_timing(struct gpmi_nfc_data *this,
+ const struct gpmi_nfc_timing *timing)
+{
+ struct nfc_hal *nfc = this->nfc;
+
+ /* Accept the new timing. */
+ nfc->timing = *timing;
+ return 0;
+}
+
+/**
+ * get_timing() - Retrieves the NFC hardware timing.
+ *
+ * @this: Per-device data.
+ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
+ * I/O transaction. If no I/O transaction is in
+ * progress, this is the clock frequency during the
+ * most recent I/O transaction.
+ * @hardware_timing: The hardware timing configuration in effect during
+ * the current I/O transaction. If no I/O transaction
+ * is in progress, this is the hardware timing
+ * configuration during the most recent I/O
+ * transaction.
+ */
+static void get_timing(struct gpmi_nfc_data *this,
+ unsigned long *clock_frequency_in_hz,
+ struct gpmi_nfc_hardware_timing *hardware_timing)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ unsigned char *gpmi_regs = resources->gpmi_regs;
+ uint32_t register_image;
+
+ /* Return the clock frequency. */
+ *clock_frequency_in_hz = nfc->clock_frequency_in_hz;
+
+ /* We'll be reading the hardware, so let's enable the clock. */
+ clk_enable(resources->clock);
+
+ /* Retrieve the hardware timing. */
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_TIMING0);
+
+ hardware_timing->data_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_SETUP) >>
+ BP_GPMI_TIMING0_DATA_SETUP;
+
+ hardware_timing->data_hold_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_HOLD) >>
+ BP_GPMI_TIMING0_DATA_HOLD;
+
+ hardware_timing->address_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_ADDRESS_SETUP) >>
+ BP_GPMI_TIMING0_ADDRESS_SETUP;
+
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_CTRL1);
+
+ hardware_timing->use_half_periods =
+ (register_image & BM_GPMI_CTRL1_HALF_PERIOD) >>
+ BP_GPMI_CTRL1_HALF_PERIOD;
+
+ hardware_timing->sample_delay_factor =
+ (register_image & BM_GPMI_CTRL1_RDN_DELAY) >>
+ BP_GPMI_CTRL1_RDN_DELAY;
+
+ /* We're done reading the hardware, so disable the clock. */
+ clk_disable(resources->clock);
+}
+
+/**
+ * exit() - Shuts down the NFC hardware.
+ *
+ * @this: Per-device data.
+ */
+static void exit(struct gpmi_nfc_data *this)
+{
+ gpmi_nfc_dma_exit(this);
+}
+
+/**
+ * begin() - Begin NFC I/O.
+ *
+ * @this: Per-device data.
+ */
+static void begin(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_hardware_timing hw;
+ unsigned char *gpmi_regs = resources->gpmi_regs;
+ unsigned int clock_period_in_ns;
+ uint32_t register_image;
+ unsigned int dll_wait_time_in_us;
+
+ /* Enable the clock. */
+ clk_enable(resources->clock);
+
+ /* Get the timing information we need. */
+ nfc->clock_frequency_in_hz = clk_get_rate(resources->clock);
+ clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz;
+
+ gpmi_nfc_compute_hardware_timing(this, &hw);
+
+ /* Set up all the simple timing parameters. */
+ register_image =
+ BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) |
+ BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) |
+ BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles) ;
+
+ __raw_writel(register_image, gpmi_regs + HW_GPMI_TIMING0);
+
+ /*
+ * HEY - PAY ATTENTION!
+ *
+ * DLL_ENABLE must be set to zero when setting RDN_DELAY or HALF_PERIOD.
+ */
+ __raw_writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* Clear out the DLL control fields. */
+ __raw_writel(BM_GPMI_CTRL1_RDN_DELAY, gpmi_regs + HW_GPMI_CTRL1_CLR);
+ __raw_writel(BM_GPMI_CTRL1_HALF_PERIOD, gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* If no sample delay is called for, return immediately. */
+ if (!hw.sample_delay_factor)
+ return;
+
+ /* Configure the HALF_PERIOD flag. */
+
+ if (hw.use_half_periods)
+ __raw_writel(BM_GPMI_CTRL1_HALF_PERIOD,
+ gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Set the delay factor. */
+ __raw_writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor),
+ gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Enable the DLL. */
+ __raw_writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /*
+ * After we enable the GPMI DLL, we have to wait 64 clock cycles before
+ * we can use the GPMI.
+ *
+ * Calculate the amount of time we need to wait, in microseconds.
+ */
+ dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000;
+
+ if (!dll_wait_time_in_us)
+ dll_wait_time_in_us = 1;
+
+ /* Wait for the DLL to settle. */
+ udelay(dll_wait_time_in_us);
+}
+
+/**
+ * end() - End NFC I/O.
+ *
+ * @this: Per-device data.
+ */
+static void end(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+
+ /* Disable the clock. */
+ clk_disable(resources->clock);
+}
+
+/**
+ * clear_bch() - Clears a BCH interrupt.
+ *
+ * @this: Per-device data.
+ */
+static void clear_bch(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+
+ __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ,
+ resources->bch_regs + HW_BCH_CTRL_CLR);
+}
+
+/**
+ * is_ready() - Returns the ready/busy status of the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ */
+static int is_ready(struct gpmi_nfc_data *this, unsigned chip)
+{
+ struct resources *resources = &this->resources;
+ uint32_t mask;
+ uint32_t register_image;
+
+ /* Extract and return the status. */
+ mask = BM_GPMI_DEBUG_READY0 << chip;
+ register_image = __raw_readl(resources->gpmi_regs + HW_GPMI_DEBUG);
+ return !!(register_image & mask);
+}
+
+/**
+ * send_command() - Sends a command and associated addresses.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that contains the command bytes.
+ * @length: The number of bytes in the buffer.
+ */
+static int send_command(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that sends out the command. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_CLE;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_READ, 1, 1, 0, 0, 1, 1, 0, 0, 3, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BM_GPMI_CTRL0_ADDRESS_INCREMENT |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * send_data() - Sends data to the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that contains the data.
+ * @length: The number of bytes in the buffer.
+ */
+static int send_data(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that writes a buffer out. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_READ, 0, 1, 0, 0, 1, 1, 0, 0, 4, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+ (*d)->cmd.pio_words[3] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * read_data() - Receives data from the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that will receive the data.
+ * @length: The number of bytes to read.
+ */
+static int read_data(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that reads the data. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_WRITE, 1, 0, 0, 0, 1, 1, 0, 0, 1, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /*
+ * A DMA descriptor that waits for the command to end and the chip to
+ * become ready.
+ *
+ * I think we actually should *not* be waiting for the chip to become
+ * ready because, after all, we don't care. I think the original code
+ * did that and no one has re-thought it yet.
+ */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 0, 1, 0, 1, 1, 1, 0, 0, 4, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(0) ;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+ (*d)->cmd.pio_words[3] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * send_page() - Sends a page, using ECC.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @payload: The physical address of the payload buffer.
+ * @auxiliary: The physical address of the auxiliary buffer.
+ */
+static int send_page(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+
+ /* A DMA descriptor that does an ECC page read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 0, 1, 0, 0, 1, 1, 0, 0, 6, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(0) ;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
+ BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
+ (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes;
+ (*d)->cmd.pio_words[4] = payload;
+ (*d)->cmd.pio_words[5] = auxiliary;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_with_bch_irq(this, dma_channel);
+}
+
+/**
+ * read_page() - Reads a page, using ECC.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @payload: The physical address of the payload buffer.
+ * @auxiliary: The physical address of the auxiliary buffer.
+ */
+static int read_page(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+
+ /* Wait for the chip to report ready. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(0) ;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Enable the BCH block and read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 0, 1, 1, 0, 0, 6, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ;
+
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
+ BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
+ (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes;
+ (*d)->cmd.pio_words[4] = payload;
+ (*d)->cmd.pio_words[5] = auxiliary;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Disable the BCH block */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 1, 1, 1, 0, 0, 3, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ;
+
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Deassert the NAND lock and interrupt. */
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0);
+ (*d)->cmd.address = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_with_bch_irq(this, dma_channel);
+}
+
+/* This structure represents the NFC HAL for this version of the hardware. */
+struct nfc_hal gpmi_nfc_hal_v0 = {
+ .version = 0,
+ .description = "4-chip GPMI and BCH",
+ .max_chip_count = 4,
+ .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
+ BP_GPMI_TIMING0_DATA_SETUP),
+ .internal_data_setup_in_ns = 0,
+ .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >>
+ BP_GPMI_CTRL1_RDN_DELAY),
+ .max_dll_clock_period_in_ns = 32,
+ .max_dll_delay_in_ns = 16,
+ .init = init,
+ .set_geometry = set_geometry,
+ .set_timing = set_timing,
+ .get_timing = get_timing,
+ .exit = exit,
+ .begin = begin,
+ .end = end,
+ .clear_bch = clear_bch,
+ .is_ready = is_ready,
+ .send_command = send_command,
+ .send_data = send_data,
+ .read_data = read_data,
+ .send_page = send_page,
+ .read_page = read_page,
+};
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c
new file mode 100644
index 000000000000..80c71c4f7817
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v1.c
@@ -0,0 +1,625 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "gpmi-nfc.h"
+#include "gpmi-nfc-gpmi-regs-v1.h"
+#include "gpmi-nfc-bch-regs-v1.h"
+
+/**
+ * init() - Initializes the NFC hardware.
+ *
+ * @this: Per-device data.
+ */
+static int init(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ int error;
+
+ /* Initialize DMA. */
+ error = gpmi_nfc_dma_init(this);
+ if (error)
+ return error;
+
+ /* Enable the clock. */
+ clk_enable(resources->clock);
+
+ /* Reset the GPMI block. */
+ mxs_reset_block(resources->gpmi_regs + HW_GPMI_CTRL0, true);
+
+ /* Choose NAND mode. */
+ __raw_writel(BM_GPMI_CTRL1_GPMI_MODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* Set the IRQ polarity. */
+ __raw_writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Disable write protection. */
+ __raw_writel(BM_GPMI_CTRL1_DEV_RESET,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Select BCH ECC. */
+ __raw_writel(BM_GPMI_CTRL1_BCH_MODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Disable the clock. */
+ clk_disable(resources->clock);
+
+ return 0;
+}
+
+/**
+ * set_geometry() - Configures the NFC geometry.
+ *
+ * @this: Per-device data.
+ */
+static int set_geometry(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_geometry *nfc = &this->nfc_geometry;
+ unsigned int block_count;
+ unsigned int block_size;
+ unsigned int metadata_size;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+
+ /* We make the abstract choices in a common function. */
+ if (gpmi_nfc_set_geometry(this))
+ return !0;
+
+ /* Translate the abstract choices into register fields. */
+ block_count = nfc->ecc_chunk_count - 1;
+ block_size = nfc->ecc_chunk_size_in_bytes;
+ metadata_size = nfc->metadata_size_in_bytes;
+ ecc_strength = nfc->ecc_strength >> 1;
+ page_size = nfc->page_size_in_bytes;
+
+ /* Enable the clock. */
+ clk_enable(resources->clock);
+
+ /*
+ * Reset the BCH block. Notice that we pass in true for the just_enable
+ * flag. This is because the soft reset for the version 0 BCH block
+ * doesn't work and the version 1 BCH block is similar enough that we
+ * suspect the same (though this has not been officially tested). If you
+ * try to soft reset a version 0 BCH block, it becomes unusable until
+ * the next hard reset.
+ */
+ mxs_reset_block(resources->bch_regs, true);
+
+ /* Configure layout 0. */
+ __raw_writel(
+ BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) |
+ BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) |
+ BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) |
+ BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size) ,
+ resources->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ __raw_writel(
+ BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
+ BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) |
+ BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size) ,
+ resources->bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* Set *all* chip selects to use layout 0. */
+ __raw_writel(0, resources->bch_regs + HW_BCH_LAYOUTSELECT);
+
+ /* Enable interrupts. */
+ __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
+ resources->bch_regs + HW_BCH_CTRL_SET);
+
+ /* Disable the clock. */
+ clk_disable(resources->clock);
+
+ return 0;
+}
+
+/**
+ * set_timing() - Configures the NFC timing.
+ *
+ * @this: Per-device data.
+ * @timing: The timing of interest.
+ */
+static int set_timing(struct gpmi_nfc_data *this,
+ const struct gpmi_nfc_timing *timing)
+{
+ struct nfc_hal *nfc = this->nfc;
+
+ /* Accept the new timing. */
+ nfc->timing = *timing;
+ return 0;
+}
+
+/**
+ * get_timing() - Retrieves the NFC hardware timing.
+ *
+ * @this: Per-device data.
+ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
+ * I/O transaction. If no I/O transaction is in
+ * progress, this is the clock frequency during the
+ * most recent I/O transaction.
+ * @hardware_timing: The hardware timing configuration in effect during
+ * the current I/O transaction. If no I/O transaction
+ * is in progress, this is the hardware timing
+ * configuration during the most recent I/O
+ * transaction.
+ */
+static void get_timing(struct gpmi_nfc_data *this,
+ unsigned long *clock_frequency_in_hz,
+ struct gpmi_nfc_hardware_timing *hardware_timing)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ unsigned char *gpmi_regs = resources->gpmi_regs;
+ uint32_t register_image;
+
+ /* Return the clock frequency. */
+
+ *clock_frequency_in_hz = nfc->clock_frequency_in_hz;
+
+ /* We'll be reading the hardware, so let's enable the clock. */
+
+ clk_enable(resources->clock);
+
+ /* Retrieve the hardware timing. */
+
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_TIMING0);
+
+ hardware_timing->data_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_SETUP) >>
+ BP_GPMI_TIMING0_DATA_SETUP;
+
+ hardware_timing->data_hold_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_HOLD) >>
+ BP_GPMI_TIMING0_DATA_HOLD;
+
+ hardware_timing->address_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_ADDRESS_SETUP) >>
+ BP_GPMI_TIMING0_ADDRESS_SETUP;
+
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_CTRL1);
+
+ hardware_timing->use_half_periods =
+ (register_image & BM_GPMI_CTRL1_HALF_PERIOD) >>
+ BP_GPMI_CTRL1_HALF_PERIOD;
+
+ hardware_timing->sample_delay_factor =
+ (register_image & BM_GPMI_CTRL1_RDN_DELAY) >>
+ BP_GPMI_CTRL1_RDN_DELAY;
+
+ /* We're done reading the hardware, so disable the clock. */
+
+ clk_disable(resources->clock);
+
+}
+
+/**
+ * exit() - Shuts down the NFC hardware.
+ *
+ * @this: Per-device data.
+ */
+static void exit(struct gpmi_nfc_data *this)
+{
+ gpmi_nfc_dma_exit(this);
+}
+
+/**
+ * begin() - Begin NFC I/O.
+ *
+ * @this: Per-device data.
+ */
+static void begin(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_hardware_timing hw;
+
+ /* Enable the clock. */
+ clk_enable(resources->clock);
+
+ /* Get the timing information we need. */
+ nfc->clock_frequency_in_hz = clk_get_rate(resources->clock);
+ gpmi_nfc_compute_hardware_timing(this, &hw);
+
+ /* Apply the hardware timing. */
+
+ /* Coming soon - the clock handling code isn't ready yet. */
+}
+
+/**
+ * end() - End NFC I/O.
+ *
+ * @this: Per-device data.
+ */
+static void end(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+
+ /* Disable the clock. */
+ clk_disable(resources->clock);
+}
+
+/**
+ * clear_bch() - Clears a BCH interrupt.
+ *
+ * @this: Per-device data.
+ */
+static void clear_bch(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+
+ __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ,
+ resources->bch_regs + HW_BCH_CTRL_CLR);
+}
+
+/**
+ * is_ready() - Returns the ready/busy status of the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ */
+static int is_ready(struct gpmi_nfc_data *this, unsigned chip)
+{
+ struct resources *resources = &this->resources;
+ uint32_t mask;
+ uint32_t register_image;
+
+ /* Extract and return the status. */
+ mask = BF_GPMI_STAT_READY_BUSY(1 << chip);
+ register_image = __raw_readl(resources->gpmi_regs + HW_GPMI_STAT);
+ return !!(register_image & mask);
+}
+
+/**
+ * send_command() - Sends a command and associated addresses.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that contains the command bytes.
+ * @length: The number of bytes in the buffer.
+ */
+static int send_command(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that sends out the command. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_CLE;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_READ, 1, 1, 0, 0, 1, 1, 0, 0, 3, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BM_GPMI_CTRL0_ADDRESS_INCREMENT |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * send_data() - Sends data to the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that contains the data.
+ * @length: The number of bytes in the buffer.
+ */
+static int send_data(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that writes a buffer out. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_READ, 0, 1, 0, 0, 1, 1, 0, 0, 4, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+ (*d)->cmd.pio_words[3] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * read_data() - Receives data from the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that will receive the data.
+ * @length: The number of bytes to read.
+ */
+static int read_data(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that reads the data. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_WRITE, 1, 0, 0, 0, 1, 1, 0, 0, 1, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /*
+ * A DMA descriptor that waits for the command to end and the chip to
+ * become ready.
+ *
+ * I think we actually should *not* be waiting for the chip to become
+ * ready because, after all, we don't care. I think the original code
+ * did that and no one has re-thought it yet.
+ */
+
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 0, 1, 0, 1, 1, 1, 0, 0, 4, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(0) ;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+ (*d)->cmd.pio_words[3] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * send_page() - Sends a page, using ECC.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @payload: The physical address of the payload buffer.
+ * @auxiliary: The physical address of the auxiliary buffer.
+ */
+static int send_page(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+
+ /* Compute the DMA channel. */
+
+ dma_channel = resources->dma_low_channel + chip;
+
+ /* A DMA descriptor that does an ECC page read. */
+
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__ENCODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 0, 1, 0, 0, 1, 1, 0, 0, 6, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(0) ;
+
+ (*d)->cmd.pio_words[1] = 0;
+
+ (*d)->cmd.pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
+ BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
+
+ (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes;
+ (*d)->cmd.pio_words[4] = payload;
+ (*d)->cmd.pio_words[5] = auxiliary;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_with_bch_irq(this, dma_channel);
+}
+
+/**
+ * read_page() - Reads a page, using ECC.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @payload: The physical address of the payload buffer.
+ * @auxiliary: The physical address of the auxiliary buffer.
+ */
+static int read_page(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+
+ /* Wait for the chip to report ready. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(0) ;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Enable the BCH block and read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__DECODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 0, 1, 1, 0, 0, 6, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
+ BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
+ (*d)->cmd.pio_words[3] = nfc_geo->page_size_in_bytes;
+ (*d)->cmd.pio_words[4] = payload;
+ (*d)->cmd.pio_words[5] = auxiliary;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Disable the BCH block */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 1, 1, 1, 0, 0, 3, 0);
+ (*d)->cmd.address = 0;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(nfc_geo->page_size_in_bytes) ;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Deassert the NAND lock and interrupt. */
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0);
+ (*d)->cmd.address = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_with_bch_irq(this, dma_channel);
+}
+
+/* This structure represents the NFC HAL for this version of the hardware. */
+
+struct nfc_hal gpmi_nfc_hal_v1 = {
+ .version = 1,
+ .description = "8-chip GPMI and BCH",
+ .max_chip_count = 8,
+ .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
+ BP_GPMI_TIMING0_DATA_SETUP),
+ .internal_data_setup_in_ns = 0,
+ .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >>
+ BP_GPMI_CTRL1_RDN_DELAY),
+ .max_dll_clock_period_in_ns = 32,
+ .max_dll_delay_in_ns = 16,
+ .init = init,
+ .set_geometry = set_geometry,
+ .set_timing = set_timing,
+ .get_timing = get_timing,
+ .exit = exit,
+ .begin = begin,
+ .end = end,
+ .clear_bch = clear_bch,
+ .is_ready = is_ready,
+ .send_command = send_command,
+ .send_data = send_data,
+ .read_data = read_data,
+ .send_page = send_page,
+ .read_page = read_page,
+};
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c
new file mode 100644
index 000000000000..a92dfa69bdf1
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-hal-v2.c
@@ -0,0 +1,918 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "gpmi-nfc.h"
+#include "gpmi-nfc-gpmi-regs-v2.h"
+#include "gpmi-nfc-bch-regs-v2.h"
+
+#define FEATURE_SIZE (4) /* p1, p2, p3, p4 */
+
+/*
+ * How many clocks do we need in low power mode?
+ * We try to list them :
+ * GMPI : gpmi_apb_clk, gpmi_io_clk
+ * BCH : bch_clk, bch_apb_clk
+ * DMA(RAM) : apbh_dma_clk, ddr_clk(RAM), ahb_max_clk(RAM)
+ * (APBHDMA fetches DMA descriptors from DDR
+ * through AHB-MAX/PL301)
+ * NAND :
+ * ONFI NAND : pll1_main_clk
+ */
+static struct clk *ddr_clk;
+static struct clk *apbh_dma_clk;
+static struct clk *ahb_max_clk;
+
+static void setup_ddr_timing_onfi(struct gpmi_nfc_data *this)
+{
+ uint32_t value;
+ struct resources *resources = &this->resources;
+
+ /* set timing 2 register */
+ value = BF_GPMI_TIMING2_DATA_PAUSE(0x6)
+ | BF_GPMI_TIMING2_CMDADD_PAUSE(0x4)
+ | BF_GPMI_TIMING2_POSTAMBLE_DELAY(0x2)
+ | BF_GPMI_TIMING2_PREAMBLE_DELAY(0x4)
+ | BF_GPMI_TIMING2_CE_DELAY(0x2)
+ | BF_GPMI_TIMING2_READ_LATENCY(0x2);
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_TIMING2);
+
+ /* set timing 1 register */
+ __raw_writel(BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(0x500),
+ resources->gpmi_regs + HW_GPMI_TIMING1);
+
+ /* Put GPMI in NAND mode, disable device reset, and make certain
+ IRQRDY polarity is active high. */
+ value = BV_GPMI_CTRL1_GPMI_MODE__NAND
+ | BM_GPMI_CTRL1_GANGED_RDYBUSY
+ | BF_GPMI_CTRL1_WRN_DLY_SEL(0x3)
+ | (BV_GPMI_CTRL1_DEV_RESET__DISABLED << 3)
+ | (BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH << 2);
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+}
+
+/* This must be called in the context of enabling necessary clocks */
+static void common_ddr_init(struct resources *resources)
+{
+ uint32_t value;
+
+ /* [6] enable both write & read DDR DLLs */
+ value = BM_GPMI_READ_DDR_DLL_CTRL_REFCLK_ON |
+ BM_GPMI_READ_DDR_DLL_CTRL_ENABLE |
+ BF_GPMI_READ_DDR_DLL_CTRL_SLV_UPDATE_INT(0x2) |
+ BF_GPMI_READ_DDR_DLL_CTRL_SLV_DLY_TARGET(0x7);
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+
+ /* [7] reset read */
+ __raw_writel(value | BM_GPMI_READ_DDR_DLL_CTRL_RESET,
+ resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+ value = value & ~BM_GPMI_READ_DDR_DLL_CTRL_RESET;
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+
+ value = BM_GPMI_WRITE_DDR_DLL_CTRL_REFCLK_ON |
+ BM_GPMI_WRITE_DDR_DLL_CTRL_ENABLE |
+ BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_UPDATE_INT(0x2) |
+ BF_GPMI_WRITE_DDR_DLL_CTRL_SLV_DLY_TARGET(0x7) ,
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+
+ /* [8] reset write */
+ __raw_writel(value | BM_GPMI_WRITE_DDR_DLL_CTRL_RESET,
+ resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+
+ /* [9] wait for locks for read and write */
+ do {
+ uint32_t read_status, write_status;
+ uint32_t r_mask, w_mask;
+
+ read_status = __raw_readl(resources->gpmi_regs
+ + HW_GPMI_READ_DDR_DLL_STS);
+ write_status = __raw_readl(resources->gpmi_regs
+ + HW_GPMI_WRITE_DDR_DLL_STS);
+
+ r_mask = (BM_GPMI_READ_DDR_DLL_STS_REF_LOCK |
+ BM_GPMI_READ_DDR_DLL_STS_SLV_LOCK);
+ w_mask = (BM_GPMI_WRITE_DDR_DLL_STS_REF_LOCK |
+ BM_GPMI_WRITE_DDR_DLL_STS_SLV_LOCK);
+
+ if (((read_status & r_mask) == r_mask)
+ && ((write_status & w_mask) == w_mask))
+ break;
+ } while (1);
+
+ /* [10] force update of read/write */
+ value = __raw_readl(resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+ __raw_writel(value | BM_GPMI_READ_DDR_DLL_CTRL_SLV_FORCE_UPD,
+ resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+
+ value = __raw_readl(resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+ __raw_writel(value | BM_GPMI_WRITE_DDR_DLL_CTRL_SLV_FORCE_UPD,
+ resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+
+ /* [11] set gate update */
+ value = __raw_readl(resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+ value |= BM_GPMI_READ_DDR_DLL_CTRL_GATE_UPDATE;
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_READ_DDR_DLL_CTRL);
+
+ value = __raw_readl(resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+ value |= BM_GPMI_WRITE_DDR_DLL_CTRL_GATE_UPDATE;
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_WRITE_DDR_DLL_CTRL);
+}
+
+static int enable_ddr_onfi(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ struct mil *mil = &this->mil;
+ struct nand_chip *nand = &this->mil.nand;
+ struct mtd_info *mtd = &mil->mtd;
+ int saved_chip_number = 0;
+ uint8_t device_feature[FEATURE_SIZE];
+ int mode = 0;/* there is 5 mode available, default is 0 */
+
+ saved_chip_number = mil->current_chip;
+ nand->select_chip(mtd, 0);
+
+ /* [0] set proper timing */
+ __raw_writel(BF_GPMI_TIMING0_ADDRESS_SETUP(0x1)
+ | BF_GPMI_TIMING0_DATA_HOLD(0x3)
+ | BF_GPMI_TIMING0_DATA_SETUP(0x3),
+ resources->gpmi_regs + HW_GPMI_TIMING0);
+
+ /* [1] send SET FEATURE commond to NAND */
+ memset(device_feature, 0, sizeof(device_feature));
+ device_feature[0] = (0x1 << 4) | (mode & 0x7);
+
+ nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ nand->cmdfunc(mtd, NAND_CMD_SET_FEATURE, 1, -1);
+ nand->write_buf(mtd, device_feature, FEATURE_SIZE);
+
+ /* [2] set clk divider */
+ __raw_writel(BM_GPMI_CTRL1_GPMI_CLK_DIV2_EN,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* [3] about the clock, pay attention! */
+ nand->select_chip(mtd, saved_chip_number);
+ {
+ struct clk *pll1;
+ pll1 = clk_get(NULL, "pll1_main_clk");
+ if (IS_ERR(pll1)) {
+ printk(KERN_INFO "No PLL1 clock\n");
+ return -EINVAL;
+ }
+ clk_set_parent(resources->clock, pll1);
+ clk_set_rate(resources->clock, 20000000);
+ }
+ nand->select_chip(mtd, 0);
+
+ /* [4] setup timing */
+ setup_ddr_timing_onfi(this);
+
+ /* [5] set to SYNC mode */
+ __raw_writel(BM_GPMI_CTRL1_TOGGLE_MODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_CLR);
+ __raw_writel(BM_GPMI_CTRL1_SSYNCMODE | BM_GPMI_CTRL1_GANGED_RDYBUSY,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* common DDR initialization */
+ common_ddr_init(resources);
+
+ nand->select_chip(mtd, saved_chip_number);
+
+ printk(KERN_INFO "Micron ONFI NAND enters synchronous mode %d\n", mode);
+ return 0;
+}
+
+static void setup_ddr_timing_toggle(struct gpmi_nfc_data *this)
+{
+ uint32_t value;
+ struct resources *resources = &this->resources;
+
+ /* set timing 2 register */
+ value = BF_GPMI_TIMING2_DATA_PAUSE(0x6)
+ | BF_GPMI_TIMING2_CMDADD_PAUSE(0x4)
+ | BF_GPMI_TIMING2_POSTAMBLE_DELAY(0x3)
+ | BF_GPMI_TIMING2_PREAMBLE_DELAY(0x2)
+ | BF_GPMI_TIMING2_CE_DELAY(0x2)
+ | BF_GPMI_TIMING2_READ_LATENCY(0x2);
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_TIMING2);
+
+ /* set timing 1 register */
+ __raw_writel(BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(0x500),
+ resources->gpmi_regs + HW_GPMI_TIMING1);
+
+ /* Put GPMI in NAND mode, disable device reset, and make certain
+ IRQRDY polarity is active high. */
+ value = BV_GPMI_CTRL1_GPMI_MODE__NAND
+ | BM_GPMI_CTRL1_GANGED_RDYBUSY
+ | (BV_GPMI_CTRL1_DEV_RESET__DISABLED << 3)
+ | (BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH << 2);
+
+ __raw_writel(value, resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+}
+
+static int enable_ddr_toggle(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ struct mil *mil = &this->mil;
+ struct nand_chip *nand = &this->mil.nand;
+ struct mtd_info *mtd = &mil->mtd;
+ int saved_chip_number = mil->current_chip;
+
+ nand->select_chip(mtd, 0);
+
+ /* [0] set proper timing */
+ __raw_writel(BF_GPMI_TIMING0_ADDRESS_SETUP(0x5)
+ | BF_GPMI_TIMING0_DATA_HOLD(0xa)
+ | BF_GPMI_TIMING0_DATA_SETUP(0xa),
+ resources->gpmi_regs + HW_GPMI_TIMING0);
+
+ /* [2] set clk divider */
+ __raw_writel(BM_GPMI_CTRL1_GPMI_CLK_DIV2_EN,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* [3] about the clock, pay attention! */
+ nand->select_chip(mtd, saved_chip_number);
+ {
+ struct clk *pll1;
+ unsigned long rate;
+
+ pll1 = clk_get(NULL, "pll1_main_clk");
+ if (IS_ERR(pll1)) {
+ printk(KERN_INFO "No PLL1 clock\n");
+ return -EINVAL;
+ }
+
+ /* toggle nand : 133/66 MHz */
+ rate = 33000000;
+ clk_set_parent(resources->clock, pll1);
+ clk_set_rate(resources->clock, rate);
+ }
+ nand->select_chip(mtd, 0);
+
+ /* [4] setup timing */
+ setup_ddr_timing_toggle(this);
+
+ /* [5] set to TOGGLE mode */
+ __raw_writel(BM_GPMI_CTRL1_SSYNCMODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_CLR);
+ __raw_writel(BM_GPMI_CTRL1_TOGGLE_MODE | BM_GPMI_CTRL1_GANGED_RDYBUSY,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* common DDR initialization */
+ common_ddr_init(resources);
+
+ nand->select_chip(mtd, saved_chip_number);
+
+ printk(KERN_INFO "-- Sumsung TOGGLE NAND is enabled now. --\n");
+ return 0;
+}
+
+/* To check if we need to initialize something else*/
+static int extra_init(struct gpmi_nfc_data *this)
+{
+ ddr_clk = clk_get(NULL, "ddr_clk");
+ if (IS_ERR(ddr_clk)) {
+ printk(KERN_ERR "The ddr clock is gone!");
+ ddr_clk = NULL;
+ return -ENOENT;
+ }
+ apbh_dma_clk = clk_get(NULL, "apbh_dma_clk");
+ if (IS_ERR(apbh_dma_clk)) {
+ printk(KERN_ERR "The APBH_DMA clock is gone!");
+ apbh_dma_clk = NULL;
+ return -ENOENT;
+ }
+ ahb_max_clk = clk_get(NULL, "ahb_max_clk");
+ if (IS_ERR(ahb_max_clk)) {
+ printk(KERN_ERR "The APBH_DMA clock is gone!");
+ ahb_max_clk = NULL;
+ return -ENOENT;
+ }
+
+ if (is_ddr_nand(&this->device_info)) {
+ switch (this->device_info.manufacturer_code) {
+ case NAND_MFR_MICRON:
+ return enable_ddr_onfi(this);
+ case NAND_MFR_SAMSUNG:
+ return enable_ddr_toggle(this);
+ default:
+ printk(KERN_ERR "You should not reach here.\n");
+ BUG();
+ }
+ }
+ return 0;
+}
+
+/**
+ * init() - Initializes the NFC hardware.
+ *
+ * @this: Per-device data.
+ */
+static int init(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ int error;
+
+ /* Initialize DMA. */
+ error = gpmi_nfc_dma_init(this);
+ if (error)
+ return error;
+
+ /* Enable the clock. */
+ clk_enable(resources->clock);
+
+ /* Reset the GPMI block. */
+ mxs_reset_block(resources->gpmi_regs + HW_GPMI_CTRL0, true);
+
+ /* Choose NAND mode. */
+ __raw_writel(BM_GPMI_CTRL1_GPMI_MODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+ /* Set the IRQ polarity. */
+ __raw_writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Disable write protection. */
+ __raw_writel(BM_GPMI_CTRL1_DEV_RESET,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Select BCH ECC. */
+ __raw_writel(BM_GPMI_CTRL1_BCH_MODE,
+ resources->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+ /* Disable the clock. */
+ clk_disable(resources->clock);
+
+ return 0;
+}
+
+/**
+ * set_geometry() - Configures the NFC geometry.
+ *
+ * @this: Per-device data.
+ */
+static int set_geometry(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_geometry *nfc = &this->nfc_geometry;
+ unsigned int block_count;
+ unsigned int block_size;
+ unsigned int metadata_size;
+ unsigned int ecc_strength;
+ unsigned int page_size;
+ uint32_t value;
+
+ /* We make the abstract choices in a common function. */
+ if (gpmi_nfc_set_geometry(this))
+ return !0;
+
+ /* Translate the abstract choices into register fields. */
+ block_count = nfc->ecc_chunk_count - 1;
+ block_size = nfc->ecc_chunk_size_in_bytes >> 2;
+ metadata_size = nfc->metadata_size_in_bytes;
+ ecc_strength = nfc->ecc_strength >> 1;
+ page_size = nfc->page_size_in_bytes;
+
+ /* Enable the clock. */
+ clk_enable(resources->clock);
+
+ /*
+ * Reset the BCH block. Notice that we pass in true for the just_enable
+ * flag. This is because the soft reset for the version 0 BCH block
+ * doesn't work and the version 1 BCH block is similar enough that we
+ * suspect the same (though this has not been officially tested). If you
+ * try to soft reset a version 0 BCH block, it becomes unusable until
+ * the next hard reset.
+ */
+ mxs_reset_block(resources->bch_regs, false);
+
+ /* Configure layout 0. */
+ value = BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) |
+ BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) |
+ BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) |
+ BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size);
+ if (is_ddr_nand(&this->device_info))
+ value |= BM_BCH_FLASH0LAYOUT0_GF13_0_GF14_1;
+
+ __raw_writel(value, resources->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+ value = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
+ BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) |
+ BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size);
+ if (is_ddr_nand(&this->device_info))
+ value |= BM_BCH_FLASH0LAYOUT1_GF13_0_GF14_1;
+
+ __raw_writel(value, resources->bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+ /* Set *all* chip selects to use layout 0. */
+ __raw_writel(0, resources->bch_regs + HW_BCH_LAYOUTSELECT);
+
+ /* Enable interrupts. */
+ __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
+ resources->bch_regs + HW_BCH_CTRL_SET);
+
+ /* Disable the clock. */
+ clk_disable(resources->clock);
+
+ return 0;
+}
+
+/**
+ * set_timing() - Configures the NFC timing.
+ *
+ * @this: Per-device data.
+ * @timing: The timing of interest.
+ */
+static int set_timing(struct gpmi_nfc_data *this,
+ const struct gpmi_nfc_timing *timing)
+{
+ struct nfc_hal *nfc = this->nfc;
+
+ /* Accept the new timing. */
+ nfc->timing = *timing;
+ return 0;
+}
+
+/**
+ * get_timing() - Retrieves the NFC hardware timing.
+ *
+ * @this: Per-device data.
+ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
+ * I/O transaction. If no I/O transaction is in
+ * progress, this is the clock frequency during the
+ * most recent I/O transaction.
+ * @hardware_timing: The hardware timing configuration in effect during
+ * the current I/O transaction. If no I/O transaction
+ * is in progress, this is the hardware timing
+ * configuration during the most recent I/O
+ * transaction.
+ */
+static void get_timing(struct gpmi_nfc_data *this,
+ unsigned long *clock_frequency_in_hz,
+ struct gpmi_nfc_hardware_timing *hardware_timing)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ unsigned char *gpmi_regs = resources->gpmi_regs;
+ uint32_t register_image;
+
+ /* Return the clock frequency. */
+ *clock_frequency_in_hz = nfc->clock_frequency_in_hz;
+
+ /* We'll be reading the hardware, so let's enable the clock. */
+ clk_enable(resources->clock);
+
+ /* Retrieve the hardware timing. */
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_TIMING0);
+
+ hardware_timing->data_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_SETUP) >>
+ BP_GPMI_TIMING0_DATA_SETUP;
+
+ hardware_timing->data_hold_in_cycles =
+ (register_image & BM_GPMI_TIMING0_DATA_HOLD) >>
+ BP_GPMI_TIMING0_DATA_HOLD;
+
+ hardware_timing->address_setup_in_cycles =
+ (register_image & BM_GPMI_TIMING0_ADDRESS_SETUP) >>
+ BP_GPMI_TIMING0_ADDRESS_SETUP;
+
+ register_image = __raw_readl(gpmi_regs + HW_GPMI_CTRL1);
+
+ hardware_timing->use_half_periods =
+ (register_image & BM_GPMI_CTRL1_HALF_PERIOD) >>
+ BP_GPMI_CTRL1_HALF_PERIOD;
+
+ hardware_timing->sample_delay_factor =
+ (register_image & BM_GPMI_CTRL1_RDN_DELAY) >>
+ BP_GPMI_CTRL1_RDN_DELAY;
+
+ /* We're done reading the hardware, so disable the clock. */
+ clk_disable(resources->clock);
+}
+
+/**
+ * exit() - Shuts down the NFC hardware.
+ *
+ * @this: Per-device data.
+ */
+static void exit(struct gpmi_nfc_data *this)
+{
+ gpmi_nfc_dma_exit(this);
+}
+
+/**
+ * begin() - Begin NFC I/O.
+ *
+ * @this: Per-device data.
+ */
+static void begin(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_hardware_timing hw;
+
+ /* Enable the clock. */
+ if (ddr_clk)
+ clk_enable(ddr_clk);
+ if (apbh_dma_clk)
+ clk_enable(apbh_dma_clk);
+ if (ahb_max_clk)
+ clk_enable(ahb_max_clk);
+ clk_enable(resources->clock);
+
+ /* Get the timing information we need. */
+ nfc->clock_frequency_in_hz = clk_get_rate(resources->clock);
+ gpmi_nfc_compute_hardware_timing(this, &hw);
+
+ /* Apply the hardware timing. */
+
+ /* Coming soon - the clock handling code isn't ready yet. */
+
+}
+
+/**
+ * end() - End NFC I/O.
+ *
+ * @this: Per-device data.
+ */
+static void end(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+
+ clk_disable(resources->clock);
+ if (ahb_max_clk)
+ clk_disable(ahb_max_clk);
+ if (apbh_dma_clk)
+ clk_disable(apbh_dma_clk);
+ if (ddr_clk)
+ clk_disable(ddr_clk);
+}
+
+/**
+ * clear_bch() - Clears a BCH interrupt.
+ *
+ * @this: Per-device data.
+ */
+static void clear_bch(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+ __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ,
+ resources->bch_regs + HW_BCH_CTRL_CLR);
+}
+
+/**
+ * is_ready() - Returns the ready/busy status of the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ */
+static int is_ready(struct gpmi_nfc_data *this, unsigned chip)
+{
+ struct resources *resources = &this->resources;
+ uint32_t mask;
+ uint32_t register_image;
+
+ /* Extract and return the status. */
+ mask = BF_GPMI_STAT_READY_BUSY(1 << 0);
+ register_image = __raw_readl(resources->gpmi_regs + HW_GPMI_STAT);
+ return !!(register_image & mask);
+}
+
+/**
+ * send_command() - Sends a command and associated addresses.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that contains the command bytes.
+ * @length: The number of bytes in the buffer.
+ */
+static int send_command(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that sends out the command. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_CLE;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_READ, 0, 1, 1, 0, 1, 1, 1, 0, 3, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BM_GPMI_CTRL0_ADDRESS_INCREMENT |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * send_data() - Sends data to the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that contains the data.
+ * @length: The number of bytes in the buffer.
+ */
+static int send_data(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that writes a buffer out. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_READ, 0, 1, 0, 0, 1, 1, 0, 0, 4, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+ (*d)->cmd.pio_words[3] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * read_data() - Receives data from the given chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @buffer: The physical address of a buffer that will receive the data.
+ * @length: The number of bytes to read.
+ */
+static int read_data(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t buffer, unsigned int length)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+
+ /* A DMA descriptor that reads the data. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ DMA_WRITE, 0, 1, 0, 0, 1, 1, 1, 0, 1, length);
+ (*d)->cmd.address = buffer;
+
+ (*d)->cmd.pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(chip) |
+ BF_GPMI_CTRL0_ADDRESS(address) |
+ BF_GPMI_CTRL0_XFER_COUNT(length) ;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_without_bch_irq(this, dma_channel);
+}
+
+/**
+ * send_page() - Sends a page, using ECC.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @payload: The physical address of the payload buffer.
+ * @auxiliary: The physical address of the auxiliary buffer.
+ */
+static int send_page(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ struct nand_device_info *info = &this->device_info;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+ uint32_t page_size;
+ uint32_t busw;
+
+ /* DDR use the 16-bit for DATA transmission! */
+ if (is_ddr_nand(info)) {
+ busw = BV_GPMI_CTRL0_WORD_LENGTH__16_BIT;
+ page_size = nfc_geo->page_size_in_bytes >> 1;
+ } else {
+ busw = BM_GPMI_CTRL0_WORD_LENGTH;
+ page_size = nfc_geo->page_size_in_bytes;
+ }
+
+ /* A DMA descriptor that does an ECC page read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__ENCODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 0, 1, 0, 0, 1, 1, 0, 0, 6, 0);
+ (*d)->cmd.address = 0;
+ (*d)->cmd.pio_words[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BF_GPMI_CTRL0_CS(chip)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(0)
+ | busw;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
+ | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+ | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask) ;
+ (*d)->cmd.pio_words[3] = page_size;
+ (*d)->cmd.pio_words[4] = payload;
+ (*d)->cmd.pio_words[5] = auxiliary;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_with_bch_irq(this, dma_channel);
+}
+
+/**
+ * read_page() - Reads a page, using ECC.
+ *
+ * @this: Per-device data.
+ * @chip: The chip of interest.
+ * @payload: The physical address of the payload buffer.
+ * @auxiliary: The physical address of the auxiliary buffer.
+ */
+static int read_page(struct gpmi_nfc_data *this, unsigned chip,
+ dma_addr_t payload, dma_addr_t auxiliary)
+{
+ struct resources *resources = &this->resources;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct nand_device_info *info = &this->device_info;
+ struct mxs_dma_desc **d = nfc->dma_descriptors;
+ int dma_channel = resources->dma_low_channel + chip;
+ uint32_t command_mode;
+ uint32_t address;
+ uint32_t ecc_command;
+ uint32_t buffer_mask;
+ uint32_t page_size;
+ uint32_t busw;
+
+ /* DDR use the 16-bit for DATA transmission! */
+ if (is_ddr_nand(info)) {
+ busw = BV_GPMI_CTRL0_WORD_LENGTH__16_BIT;
+ page_size = nfc_geo->page_size_in_bytes >> 1;
+ } else {
+ busw = BM_GPMI_CTRL0_WORD_LENGTH;
+ page_size = nfc_geo->page_size_in_bytes;
+ }
+
+ /* Wait for the chip to report ready. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0);
+ (*d)->cmd.address = 0;
+ (*d)->cmd.pio_words[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BF_GPMI_CTRL0_CS(chip)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(0)
+ | busw;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Enable the BCH block and read. */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+ ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__DECODE;
+ buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+ BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 0, 0, 1, 0, 0, 6, 0);
+ (*d)->cmd.address = 0;
+ (*d)->cmd.pio_words[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BF_GPMI_CTRL0_CS(chip)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(page_size)
+ | busw;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
+ | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+ | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+ (*d)->cmd.pio_words[3] = page_size;
+ (*d)->cmd.pio_words[4] = payload;
+ (*d)->cmd.pio_words[5] = auxiliary;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Disable the BCH block */
+ command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+ address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 1, 0, 0, 1, 0, 1, 0, 0, 3, 0);
+ (*d)->cmd.address = 0;
+ (*d)->cmd.pio_words[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+ | BF_GPMI_CTRL0_CS(chip)
+ | BF_GPMI_CTRL0_ADDRESS(address)
+ | BF_GPMI_CTRL0_XFER_COUNT(page_size)
+ | busw;
+ (*d)->cmd.pio_words[1] = 0;
+ (*d)->cmd.pio_words[2] = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+ d++;
+
+ /* Deassert the NAND lock and interrupt. */
+ fill_dma_word1(&(*d)->cmd.cmd,
+ NO_DMA_XFER, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0);
+ (*d)->cmd.address = 0;
+
+ mxs_dma_desc_append(dma_channel, (*d));
+
+ return start_dma_with_bch_irq(this, dma_channel);
+}
+
+/* This structure represents the NFC HAL for this version of the hardware. */
+struct nfc_hal gpmi_nfc_hal_v2 = {
+ .version = 2,
+ .description = "8-chip GPMI and BCH",
+ .max_chip_count = 8,
+ .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
+ BP_GPMI_TIMING0_DATA_SETUP),
+ .internal_data_setup_in_ns = 0,
+ .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >>
+ BP_GPMI_CTRL1_RDN_DELAY),
+ .max_dll_clock_period_in_ns = 32,
+ .max_dll_delay_in_ns = 16,
+ .init = init,
+ .extra_init = extra_init,
+ .set_geometry = set_geometry,
+ .set_timing = set_timing,
+ .get_timing = get_timing,
+ .exit = exit,
+ .begin = begin,
+ .end = end,
+ .clear_bch = clear_bch,
+ .is_ready = is_ready,
+ .send_command = send_command,
+ .send_data = send_data,
+ .read_data = read_data,
+ .send_page = send_page,
+ .read_page = read_page,
+};
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c
new file mode 100644
index 000000000000..32bc08160987
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-main.c
@@ -0,0 +1,1889 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include "gpmi-nfc.h"
+#include "linux/slab.h"
+
+static int enable_gpmi_nand = { 0 };
+
+/*
+ * This structure contains the "safe" GPMI timing that should succeed with any
+ * NAND Flash device (although, with less-than-optimal performance).
+ */
+
+static struct gpmi_nfc_timing safe_timing = {
+ .data_setup_in_ns = 80,
+ .data_hold_in_ns = 60,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+};
+
+/*
+ * This array has a pointer to every NFC HAL structure. The probing process will
+ * find and install the one that matches the version given by the platform.
+ */
+
+static struct nfc_hal *(nfc_hals[]) = {
+ /* i.mx23 */
+ &gpmi_nfc_hal_v0,
+ /* i.mx28 */
+ &gpmi_nfc_hal_v1,
+ /* i.mx50 */
+ &gpmi_nfc_hal_v2,
+};
+
+/*
+ * This array has a pointer to every Boot ROM Helper structure. The probing
+ * process will find and install the one that matches the version given by the
+ * platform.
+ */
+
+static struct boot_rom_helper *(boot_rom_helpers[]) = {
+ &gpmi_nfc_boot_rom_helper_v0,
+ &gpmi_nfc_boot_rom_helper_v1,
+};
+
+/**
+ * show_device_report() - Contains a shell script that creates a handy report.
+ *
+ * @d: 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_report(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ static const char *script =
+ "GPMISysDirectory=/sys/bus/platform/devices/gpmi-nfc.0\n"
+ "\n"
+ "NodeList='\n"
+ "physical_geometry\n"
+ "nfc_info\n"
+ "nfc_geometry\n"
+ "timing\n"
+ "timing_diagram\n"
+ "rom_geometry\n"
+ "mtd_nand_info\n"
+ "mtd_info\n"
+ "'\n"
+ "\n"
+ "cd ${GPMISysDirectory}\n"
+ "\n"
+ "printf '\\n'\n"
+ "\n"
+ "for NodeName in ${NodeList}\n"
+ "do\n"
+ "\n"
+ " printf '--------------------------------------------\\n'\n"
+ " printf '%s\\n' ${NodeName}\n"
+ " printf '--------------------------------------------\\n'\n"
+ " printf '\\n'\n"
+ "\n"
+ " cat ${NodeName}\n"
+ "\n"
+ " printf '\\n'\n"
+ "\n"
+ "done\n"
+ ;
+
+ return sprintf(buf, "%s", script);
+
+}
+
+/**
+ * show_device_numchips() - Shows the number of physical chips.
+ *
+ * This node is made obsolete by the physical_geometry node, but we keep it for
+ * backward compatibility (especially for kobs).
+ *
+ * @d: 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_numchips(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct nand_chip *nand = &this->mil.nand;
+
+ return sprintf(buf, "%d\n", nand->numchips);
+}
+
+/**
+ * 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)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct nfc_hal *nfc = this->nfc;
+
+ return sprintf(buf,
+ "Version : %u\n"
+ "Description : %s\n"
+ "Max Chip Count : %u\n"
+ "Max Data Setup Cycles : 0x%x\n"
+ "Internal Data Setup in ns : %u\n"
+ "Max Sample Delay Factor : 0x%x\n"
+ "Max DLL Clock Period in ns: %u\n"
+ "Max DLL Delay in ns : %u\n"
+ ,
+ nfc->version,
+ nfc->description,
+ nfc->max_chip_count,
+ nfc->max_data_setup_cycles,
+ nfc->internal_data_setup_in_ns,
+ nfc->max_sample_delay_factor,
+ nfc->max_dll_clock_period_in_ns,
+ nfc->max_dll_delay_in_ns
+ );
+
+}
+
+/**
+ * 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 gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct nfc_geometry *nfc = &this->nfc_geometry;
+
+ return sprintf(buf,
+ "ECC Algorithm : %s\n"
+ "ECC Strength : %u\n"
+ "Page Size in Bytes : %u\n"
+ "Metadata Size in Bytes : %u\n"
+ "ECC Chunk Size in Bytes: %u\n"
+ "ECC Chunk Count : %u\n"
+ "Payload Size in Bytes : %u\n"
+ "Auxiliary Size in Bytes: %u\n"
+ "Auxiliary Status Offset: %u\n"
+ "Block Mark Byte Offset : %u\n"
+ "Block Mark Bit Offset : %u\n"
+ ,
+ nfc->ecc_algorithm,
+ nfc->ecc_strength,
+ nfc->page_size_in_bytes,
+ nfc->metadata_size_in_bytes,
+ nfc->ecc_chunk_size_in_bytes,
+ nfc->ecc_chunk_count,
+ nfc->payload_size_in_bytes,
+ nfc->auxiliary_size_in_bytes,
+ nfc->auxiliary_status_offset,
+ nfc->block_mark_byte_offset,
+ nfc->block_mark_bit_offset
+ );
+
+}
+
+/**
+ * show_device_rom_geometry() - Shows the Boot ROM Helper's 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_rom_geometry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct boot_rom_geometry *rom = &this->rom_geometry;
+
+ return sprintf(buf,
+ "Boot Area Count : %u\n"
+ "Boot Area Size in Bytes : %u\n"
+ "Stride Size in Pages : %u\n"
+ "Seach Area Stride Exponent: %u\n"
+ ,
+ rom->boot_area_count,
+ rom->boot_area_size_in_bytes,
+ rom->stride_size_in_pages,
+ rom->search_area_stride_exponent
+ );
+
+}
+
+/**
+ * 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 gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct mil *mil = &this->mil;
+ struct nand_chip *nand = &mil->nand;
+
+ o += sprintf(buf + o,
+ "Options : 0x%08x\n"
+ "Chip Count : %u\n"
+ "Chip Size in Bytes : %llu\n"
+ "Minimum Writable Size in Bytes: %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 gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+
+ o += sprintf(buf + o,
+ "Name : %s\n"
+ "Type : %u\n"
+ "Flags : 0x%08x\n"
+ "Size in Bytes : %llu\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_timing_diagram() - Shows a timing diagram.
+ *
+ * @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_timing_diagram(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_timing timing = nfc->timing;
+ struct gpmi_nfc_hardware_timing hardware_timing;
+ unsigned long clock_frequency_in_hz;
+ unsigned long clock_period_in_ns;
+ unsigned int data_setup_in_ns;
+ unsigned int dll_delay_shift;
+ unsigned int sample_delay_in_ns;
+ unsigned int tDS_in_ns;
+ unsigned int tOPEN_in_ns;
+ unsigned int tCLOSE_in_ns;
+ unsigned int tEYE_in_ns;
+ unsigned int tDELAY_in_ns;
+ unsigned int tDS;
+ unsigned int tOPEN;
+ unsigned int tCLOSE;
+ unsigned int tEYE;
+ unsigned int tDELAY;
+ const unsigned int diagram_width_in_chars = 55;
+ unsigned int diagram_width_in_ns;
+ int o = 0;
+ unsigned int i;
+
+ /*
+ * If there are any timing characteristics we need, but don't know, we
+ * pretend they're zero.
+ */
+
+ if (timing.tREA_in_ns < 0)
+ timing.tREA_in_ns = 0;
+
+ if (timing.tRHOH_in_ns < 0)
+ timing.tRHOH_in_ns = 0;
+
+ /* Get information about the current/last I/O transaction. */
+
+ nfc->get_timing(this, &clock_frequency_in_hz, &hardware_timing);
+
+ clock_period_in_ns = 1000000000 / clock_frequency_in_hz;
+
+ /* Compute basic timing facts. */
+
+ data_setup_in_ns =
+ hardware_timing.data_setup_in_cycles * clock_period_in_ns;
+
+ /* Compute data sample delay facts. */
+
+ dll_delay_shift = 3;
+
+ if (hardware_timing.use_half_periods)
+ dll_delay_shift++;
+
+ sample_delay_in_ns =
+ (hardware_timing.sample_delay_factor * clock_period_in_ns) >>
+ dll_delay_shift;
+
+ /* Compute the basic metrics in the diagram, in nanoseconds. */
+
+ tDS_in_ns = data_setup_in_ns;
+ tOPEN_in_ns = pdata->max_prop_delay_in_ns + timing.tREA_in_ns;
+ tCLOSE_in_ns = pdata->min_prop_delay_in_ns + timing.tRHOH_in_ns;
+ tEYE_in_ns = tDS_in_ns + tCLOSE_in_ns - tOPEN_in_ns;
+ tDELAY_in_ns = sample_delay_in_ns;
+
+ /*
+ * We need to translate nanosecond timings into character widths in the
+ * diagram. The first step is to discover how "wide" the diagram is in
+ * nanoseconds. That depends on which happens latest: the sample point
+ * or the close of the eye.
+ */
+
+ if (tCLOSE_in_ns >= tDELAY_in_ns)
+ diagram_width_in_ns = tDS_in_ns + tCLOSE_in_ns;
+ else
+ diagram_width_in_ns = tDS_in_ns + tDELAY_in_ns;
+
+ /* Convert the metrics that appear in the diagram. */
+
+ tDS = (tDS_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+ tOPEN = (tOPEN_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+ tCLOSE = (tCLOSE_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+ tEYE = (tEYE_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+ tDELAY = (tDELAY_in_ns * diagram_width_in_chars) / diagram_width_in_ns;
+
+ /*
+ * Show the results.
+ *
+ * This code is really ugly, but it draws a pretty picture :)
+ */
+
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "Sample ______");
+ for (i = 0; i < tDS; i++)
+ o += sprintf(buf + o, "_");
+ if (tDELAY > 0)
+ for (i = 0; i < (tDELAY - 1); i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "|");
+ for (i = 0; i < (diagram_width_in_chars - (tDS + tDELAY)); i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "Strobe ");
+ for (i = 0; i < tDS; i++)
+ o += sprintf(buf + o, " ");
+ o += sprintf(buf + o, "|");
+ if (tDELAY > 1) {
+ for (i = 2; i < tDELAY; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, " tDELAY\n");
+
+
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " tDS ");
+ o += sprintf(buf + o, "|");
+ if (tDS > 1) {
+ for (i = 2; i < tDS; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " ______");
+ for (i = 0; i < tDS; i++)
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < (diagram_width_in_chars - tDS); i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "RDN ");
+ if (tDS > 0) {
+ if (tDS == 1)
+ o += sprintf(buf + o, "V");
+ else {
+ o += sprintf(buf + o, "\\");
+ for (i = 2; i < tDS; i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "/");
+ }
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " tOPEN ");
+ o += sprintf(buf + o, "|");
+ if (tOPEN > 1) {
+ for (i = 2; i < tOPEN; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < tDS; i++)
+ o += sprintf(buf + o, " ");
+ o += sprintf(buf + o, "|");
+ if (tCLOSE > 1) {
+ for (i = 2; i < tCLOSE; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, " tCLOSE\n");
+
+
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < tOPEN; i++)
+ o += sprintf(buf + o, " ");
+ if (tEYE > 2) {
+ o += sprintf(buf + o, " ");
+ for (i = 2; i < tEYE; i++)
+ o += sprintf(buf + o, "_");
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, "Data ______");
+ for (i = 0; i < tOPEN; i++)
+ o += sprintf(buf + o, "_");
+ if (tEYE > 0) {
+ if (tEYE == 1)
+ o += sprintf(buf + o, "|");
+ else {
+ o += sprintf(buf + o, "/");
+ for (i = 2; i < tEYE; i++)
+ o += sprintf(buf + o, " ");
+ o += sprintf(buf + o, "\\");
+ }
+ }
+ for (i = 0; i < (diagram_width_in_chars - (tOPEN + tEYE)); i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < tOPEN; i++)
+ o += sprintf(buf + o, " ");
+ if (tEYE > 0) {
+ if (tEYE == 1)
+ o += sprintf(buf + o, "|");
+ else {
+ o += sprintf(buf + o, "\\");
+ for (i = 2; i < tEYE; i++)
+ o += sprintf(buf + o, "_");
+ o += sprintf(buf + o, "/");
+ }
+ }
+ o += sprintf(buf + o, "\n");
+
+
+ o += sprintf(buf + o, " ");
+ for (i = 0; i < tOPEN; i++)
+ o += sprintf(buf + o, " ");
+ o += sprintf(buf + o, "|");
+ if (tEYE > 1) {
+ for (i = 2; i < tEYE; i++)
+ o += sprintf(buf + o, "-");
+ o += sprintf(buf + o, "|");
+ }
+ o += sprintf(buf + o, " tEYE\n");
+
+
+ o += sprintf(buf + o, "\n");
+ o += sprintf(buf + o, "tDS : %u ns\n", tDS_in_ns);
+ o += sprintf(buf + o, "tOPEN : %u ns\n", tOPEN_in_ns);
+ o += sprintf(buf + o, "tCLOSE: %u ns\n", tCLOSE_in_ns);
+ o += sprintf(buf + o, "tEYE : %u ns\n", tEYE_in_ns);
+ o += sprintf(buf + o, "tDELAY: %u ns\n", tDELAY_in_ns);
+ o += sprintf(buf + o, "\n");
+
+
+ return o;
+
+}
+
+/**
+ * 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 gpmi_nfc_data *this = dev_get_drvdata(dev);
+
+ /* Invalidate the page cache. */
+
+ this->mil.nand.pagebuf = -1;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/**
+ * store_device_mark_block_bad() - Marks a block as bad.
+ *
+ * @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_mark_block_bad(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+ struct nand_chip *nand = &mil->nand;
+ unsigned long block_number;
+ loff_t byte_address;
+ int error;
+
+ /* Look for nonsense. */
+
+ if (!size)
+ return -EINVAL;
+
+ /* Try to understand the block number. */
+
+ if (strict_strtoul(buf, 0, &block_number))
+ return -EINVAL;
+
+ /* Compute the byte address of this block. */
+
+ byte_address = block_number << nand->phys_erase_shift;
+
+ /* Attempt to mark the block bad. */
+
+ error = mtd->block_markbad(mtd, byte_address);
+
+ if (error)
+ return error;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/**
+ * show_device_ignorebad() - Shows the value of the 'ignorebad' flag.
+ *
+ * @d: 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_ignorebad(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct mil *mil = &this->mil;
+
+ return sprintf(buf, "%d\n", mil->ignore_bad_block_marks);
+}
+
+/**
+ * store_device_ignorebad() - Sets the value of the 'ignorebad' 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_ignorebad(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct mil *mil = &this->mil;
+ const char *p = buf;
+ unsigned long v;
+
+ /* Try to make sense of what arrived from user space. */
+
+ if (strict_strtoul(p, 0, &v) < 0)
+ return size;
+
+ if (v > 0)
+ v = 1;
+
+ /* Only do something if the value is changing. */
+
+ if (v != mil->ignore_bad_block_marks) {
+
+ if (v) {
+
+ /*
+ * If control arrives here, we want to begin ignoring
+ * bad block marks. Reach into the NAND Flash MTD data
+ * structures and set the in-memory BBT pointer to NULL.
+ * This will cause the NAND Flash MTD code to believe
+ * that it never created a BBT and force it to call our
+ * block_bad function.
+ *
+ * See mil_block_bad for more details.
+ */
+
+ mil->saved_bbt = mil->nand.bbt;
+ mil->nand.bbt = 0;
+
+ } else {
+
+ /*
+ * If control arrives here, we want to stop ignoring
+ * bad block marks. Restore the NAND Flash MTD's pointer
+ * to its in-memory BBT.
+ */
+
+ mil->nand.bbt = mil->saved_bbt;
+
+ }
+
+ mil->ignore_bad_block_marks = v;
+
+ }
+
+ 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 gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct mil *mil = &this->mil;
+
+ return sprintf(buf, "%d\n", mil->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)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct mil *mil = &this->mil;
+ long new_inject_ecc_error;
+
+ /* Look for nonsense. */
+
+ if (!size)
+ return -EINVAL;
+
+ /* Try to understand the ECC error count. */
+
+ if (strict_strtol(buf, 0, &new_inject_ecc_error))
+ return -EINVAL;
+
+ /* Store the value. */
+
+ mil->inject_ecc_error = new_inject_ecc_error;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/**
+ * show_device_timing_help() - Show help for setting timing.
+ *
+ * @d: 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_timing_help(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ static const char *help =
+ "<Data Setup>,<Data Hold>,<Address Setup>,<Sample Delay>,"
+ "<tREA>,<tRLOH>,<tRHOH>\n";
+
+ return sprintf(buf, "%s", help);
+
+}
+
+/**
+ * show_device_timing() - Shows the current timing.
+ *
+ * @d: 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_timing(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct nfc_hal *nfc = this->nfc;
+ struct gpmi_nfc_timing *recorded = &nfc->timing;
+ unsigned long clock_frequency_in_hz;
+ unsigned long clock_period_in_ns;
+ struct gpmi_nfc_hardware_timing hardware;
+ unsigned int effective_data_setup_in_ns;
+ unsigned int effective_data_hold_in_ns;
+ unsigned int effective_address_setup_in_ns;
+ unsigned int dll_delay_shift;
+ unsigned int effective_sample_delay_in_ns;
+
+ /* Get information about the current/last I/O transaction. */
+
+ nfc->get_timing(this, &clock_frequency_in_hz, &hardware);
+
+ clock_period_in_ns = 1000000000 / clock_frequency_in_hz;
+
+ /* Compute basic timing facts. */
+
+ effective_data_setup_in_ns =
+ hardware.data_setup_in_cycles * clock_period_in_ns;
+ effective_data_hold_in_ns =
+ hardware.data_hold_in_cycles * clock_period_in_ns;
+ effective_address_setup_in_ns =
+ hardware.address_setup_in_cycles * clock_period_in_ns;
+
+ /* Compute data sample delay facts. */
+
+ dll_delay_shift = 3;
+
+ if (hardware.use_half_periods)
+ dll_delay_shift++;
+
+ effective_sample_delay_in_ns =
+ (hardware.sample_delay_factor * clock_period_in_ns) >>
+ dll_delay_shift;
+
+ /* Show the results. */
+
+ return sprintf(buf,
+ "Minimum Propagation Delay in ns : %u\n"
+ "Maximum Propagation Delay in ns : %u\n"
+ "Clock Frequency in Hz : %lu\n"
+ "Clock Period in ns : %lu\n"
+ "Recorded Data Setup in ns : %d\n"
+ "Hardware Data Setup in cycles : %u\n"
+ "Effective Data Setup in ns : %u\n"
+ "Recorded Data Hold in ns : %d\n"
+ "Hardware Data Hold in cycles : %u\n"
+ "Effective Data Hold in ns : %u\n"
+ "Recorded Address Setup in ns : %d\n"
+ "Hardware Address Setup in cycles: %u\n"
+ "Effective Address Setup in ns : %u\n"
+ "Using Half Period : %s\n"
+ "Recorded Sample Delay in ns : %d\n"
+ "Hardware Sample Delay Factor : %u\n"
+ "Effective Sample Delay in ns : %u\n"
+ "Recorded tREA in ns : %d\n"
+ "Recorded tRLOH in ns : %d\n"
+ "Recorded tRHOH in ns : %d\n"
+ ,
+ pdata->min_prop_delay_in_ns,
+ pdata->max_prop_delay_in_ns,
+ clock_frequency_in_hz,
+ clock_period_in_ns,
+ recorded->data_setup_in_ns,
+ hardware .data_setup_in_cycles,
+ effective_data_setup_in_ns,
+ recorded->data_hold_in_ns,
+ hardware .data_hold_in_cycles,
+ effective_data_hold_in_ns,
+ recorded->address_setup_in_ns,
+ hardware .address_setup_in_cycles,
+ effective_address_setup_in_ns,
+ hardware .use_half_periods ? "Yes" : "No",
+ recorded->gpmi_sample_delay_in_ns,
+ hardware .sample_delay_factor,
+ effective_sample_delay_in_ns,
+ recorded->tREA_in_ns,
+ recorded->tRLOH_in_ns,
+ recorded->tRHOH_in_ns);
+
+}
+
+/**
+ * store_device_timing() - Sets the current timing.
+ *
+ * @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_timing(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct gpmi_nfc_data *this = dev_get_drvdata(dev);
+ struct nfc_hal *nfc = this->nfc;
+ const char *p = buf;
+ const char *q;
+ char tmps[20];
+ long t;
+ struct gpmi_nfc_timing new;
+
+ int8_t *field_pointers[] = {
+ &new.data_setup_in_ns,
+ &new.data_hold_in_ns,
+ &new.address_setup_in_ns,
+ &new.gpmi_sample_delay_in_ns,
+ &new.tREA_in_ns,
+ &new.tRLOH_in_ns,
+ &new.tRHOH_in_ns,
+ NULL,
+ };
+
+ int8_t **field_pointer = field_pointers;
+
+ /*
+ * Loop over comma-separated timing values in the incoming buffer,
+ * assigning them to fields in the timing structure as we go along.
+ */
+
+ while (*field_pointer != NULL) {
+
+ /* Clear out the temporary buffer. */
+
+ memset(tmps, 0, sizeof(tmps));
+
+ /* Copy the timing value into the temporary buffer. */
+
+ q = strchr(p, ',');
+ if (q)
+ strncpy(tmps, p, min_t(int, sizeof(tmps) - 1, q - p));
+ else
+ strncpy(tmps, p, sizeof(tmps) - 1);
+
+ /* Attempt to convert the current timing value. */
+
+ if (strict_strtol(tmps, 0, &t) < 0)
+ return -EINVAL;
+
+ if ((t > 127) || (t < -128))
+ return -EINVAL;
+
+ /* Assign this value to the current field. */
+
+ **field_pointer = (int8_t) t;
+ field_pointer++;
+
+ /* Check if we ran out of input too soon. */
+
+ if (!q && *field_pointer)
+ return -EINVAL;
+
+ /* Move past the comma to the next timing value. */
+
+ p = q + 1;
+
+ }
+
+ /* Hand over the timing to the NFC. */
+
+ nfc->set_timing(this, &new);
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/* Device attributes that appear in sysfs. */
+
+static DEVICE_ATTR(report , 0555, show_device_report , 0);
+static DEVICE_ATTR(numchips , 0444, show_device_numchips , 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(rom_geometry , 0444, show_device_rom_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(timing_diagram , 0444, show_device_timing_diagram , 0);
+static DEVICE_ATTR(timing_help , 0444, show_device_timing_help , 0);
+
+static DEVICE_ATTR(invalidate_page_cache, 0644,
+ 0, store_device_invalidate_page_cache);
+
+static DEVICE_ATTR(mark_block_bad, 0200,
+ 0, store_device_mark_block_bad);
+
+static DEVICE_ATTR(ignorebad, 0644,
+ show_device_ignorebad, store_device_ignorebad);
+
+static DEVICE_ATTR(inject_ecc_error, 0644,
+ show_device_inject_ecc_error, store_device_inject_ecc_error);
+
+static DEVICE_ATTR(timing, 0644,
+ show_device_timing, store_device_timing);
+
+static struct device_attribute *device_attributes[] = {
+ &dev_attr_report,
+ &dev_attr_numchips,
+ &dev_attr_nfc_info,
+ &dev_attr_nfc_geometry,
+ &dev_attr_rom_geometry,
+ &dev_attr_mtd_nand_info,
+ &dev_attr_mtd_info,
+ &dev_attr_invalidate_page_cache,
+ &dev_attr_mark_block_bad,
+ &dev_attr_ignorebad,
+ &dev_attr_inject_ecc_error,
+ &dev_attr_timing,
+ &dev_attr_timing_help,
+ &dev_attr_timing_diagram,
+};
+
+/**
+ * validate_the_platform() - Validates information about the platform.
+ *
+ * @pdev: A pointer to the platform device data structure.
+ */
+static int validate_the_platform(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpmi_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;
+
+}
+
+/**
+ * acquire_register_block() - Tries to acquire and map a register block.
+ *
+ * @this: Per-device data.
+ * @resource_name: The name of the resource.
+ * @reg_block_base: A pointer to a variable that will receive the address of
+ * the mapped register block.
+ */
+static int acquire_register_block(struct gpmi_nfc_data *this,
+ const char *resource_name, void **reg_block_base)
+{
+ struct platform_device *pdev = this->pdev;
+ struct device *dev = this->dev;
+ void *p;
+ struct resource *r;
+
+ /* Attempt to get information about the given resource. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, resource_name);
+
+ if (!r) {
+ dev_err(dev, "Can't get resource information for '%s'\n",
+ resource_name);
+ return -ENXIO;
+ }
+
+ /* Attempt to remap the register block. */
+
+ p = ioremap(r->start, r->end - r->start + 1);
+
+ if (!p) {
+ dev_err(dev, "Can't remap %s\n", resource_name);
+ return -EIO;
+ }
+
+ /* If control arrives here, everything went fine. */
+
+ *reg_block_base = p;
+
+ return 0;
+
+}
+
+/**
+ * release_register_block() - Releases a register block.
+ *
+ * @this: Per-device data.
+ * @reg_block_base: A pointer to the mapped register block.
+ */
+static void release_register_block(struct gpmi_nfc_data *this,
+ void *reg_block_base)
+{
+ iounmap(reg_block_base);
+}
+
+/**
+ * acquire_interrupt() - Tries to acquire an interrupt.
+ *
+ * @this: Per-device data.
+ * @resource_name: The name of the resource.
+ * @interrupt_handler: A pointer to the function that will handle interrupts
+ * from this interrupt number.
+ * @lno: A pointer to a variable that will receive the acquired
+ * interrupt number(low part).
+ * @hno: A pointer to a variable that will receive the acquired
+ * interrupt number(high part).
+ */
+static int acquire_interrupt(
+ struct gpmi_nfc_data *this, const char *resource_name,
+ irq_handler_t interrupt_handler, int *lno, int *hno)
+{
+ struct platform_device *pdev = this->pdev;
+ struct device *dev = this->dev;
+ int error = 0;
+ struct resource *r;
+ int i;
+
+ /* Attempt to get information about the given resource. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, resource_name);
+
+ if (!r) {
+ dev_err(dev, "Can't get resource information for '%s'\n",
+ resource_name);
+ return -ENXIO;
+ }
+
+ /* Attempt to own the interrupt. */
+ for (i = r->start; i <= r->end; i++) {
+ error = request_irq(i, interrupt_handler, 0,
+ resource_name, this);
+
+ if (error) {
+ dev_err(dev, "Can't own %s\n", resource_name);
+
+ /* Free all the irq's we've already acquired. */
+
+ while ((i - r->start) >= 0) {
+ free_irq(i, this);
+ i--;
+ }
+
+ return -EIO;
+ }
+ }
+
+ /* If control arrives here, everything went fine. */
+
+ *lno = r->start;
+ *hno = r->end;
+
+ return 0;
+
+}
+
+/**
+ * release_interrupt() - Releases an interrupt.
+ *
+ * @this: Per-device data.
+ * @interrupt_number: The interrupt number.
+ */
+static void release_interrupt(struct gpmi_nfc_data *this,
+ int low_interrupt_number, int high_interrupt_number)
+{
+ int i;
+ for (i = low_interrupt_number; i <= high_interrupt_number; i++)
+ free_irq(i, this);
+}
+
+/**
+ * acquire_dma_channels() - Tries to acquire DMA channels.
+ *
+ * @this: Per-device data.
+ * @resource_name: The name of the resource.
+ * @low_channel: A pointer to a variable that will receive the acquired
+ * low DMA channel number.
+ * @high_channel: A pointer to a variable that will receive the acquired
+ * high DMA channel number.
+ */
+static int acquire_dma_channels(
+ struct gpmi_nfc_data *this, const char *resource_name,
+ unsigned *low_channel, unsigned *high_channel)
+{
+ struct platform_device *pdev = this->pdev;
+ struct device *dev = this->dev;
+ int error = 0;
+ struct resource *r;
+ unsigned int dma_channel;
+
+ /* Attempt to get information about the given resource. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_DMA, resource_name);
+
+ if (!r) {
+ dev_err(dev, "Can't get resource information for '%s'\n",
+ resource_name);
+ return -ENXIO;
+ }
+
+ /* Loop over DMA channels, attempting to own them. */
+
+ for (dma_channel = r->start; dma_channel <= r->end; dma_channel++) {
+
+ /* Attempt to own the current channel. */
+
+ error = mxs_dma_request(dma_channel, dev, resource_name);
+
+ /* Check if we successfully acquired the current channel. */
+
+ if (error) {
+
+ dev_err(dev, "Can't acquire DMA channel %u\n",
+ dma_channel);
+
+ /* Free all the channels we've already acquired. */
+
+ while (--dma_channel >= 0)
+ mxs_dma_release(dma_channel, dev);
+
+ return error;
+
+ }
+
+ /*
+ * If control arrives here, we successfully acquired the
+ * current channel. Continue initializing it.
+ */
+
+ mxs_dma_reset(dma_channel);
+ mxs_dma_ack_irq(dma_channel);
+
+ }
+
+ /* If control arrives here, all went well. */
+
+ *low_channel = r->start;
+ *high_channel = r->end;
+
+ return 0;
+
+}
+
+/**
+ * release_dma_channels() - Releases DMA channels.
+ *
+ * @this: Per-device data.
+ * @low_channel: The low DMA channel number.
+ * @high_channel: The high DMA channel number.
+ */
+static void release_dma_channels(struct gpmi_nfc_data *this,
+ unsigned low_channel, unsigned high_channel)
+{
+ struct device *dev = this->dev;
+ unsigned int i;
+
+ for (i = low_channel; i <= high_channel; i++)
+ mxs_dma_release(i, dev);
+}
+
+/**
+ * acquire_clock() - Tries to acquire a clock.
+ *
+ * @this: Per-device data.
+ * @resource_name: The name of the clock.
+ * @high_channel: A pointer to a variable that will receive the acquired
+ * clock address.
+ */
+static int acquire_clock(struct gpmi_nfc_data *this,
+ const char *clock_name, struct clk **clock)
+{
+ struct device *dev = this->dev;
+ int error = 0;
+ struct clk *c;
+
+ /* Try to get the clock. */
+
+ c = clk_get(dev, clock_name);
+
+ if (IS_ERR(c)) {
+ error = PTR_ERR(c);
+ dev_err(dev, "Can't own clock %s\n", clock_name);
+ return error;
+ }
+
+ /* If control arrives here, everything went fine. */
+
+ *clock = c;
+
+ return 0;
+
+}
+
+/**
+ * release_clock() - Releases a clock.
+ *
+ * @this: Per-device data.
+ * @clock: A pointer to the clock structure.
+ */
+static void release_clock(struct gpmi_nfc_data *this, struct clk *clock)
+{
+ clk_disable(clock);
+ clk_put(clock);
+}
+
+/**
+ * acquire_resources() - Tries to acquire resources.
+ *
+ * @this: Per-device data.
+ */
+static int acquire_resources(struct gpmi_nfc_data *this)
+{
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct resources *resources = &this->resources;
+ int error = 0;
+
+ /* Attempt to acquire the GPMI register block. */
+
+ error = acquire_register_block(this,
+ GPMI_NFC_GPMI_REGS_ADDR_RES_NAME, &(resources->gpmi_regs));
+
+ if (error)
+ goto exit_gpmi_regs;
+
+ /* Attempt to acquire the BCH register block. */
+
+ error = acquire_register_block(this,
+ GPMI_NFC_BCH_REGS_ADDR_RES_NAME, &(resources->bch_regs));
+
+ if (error)
+ goto exit_bch_regs;
+
+ /* Attempt to acquire the BCH interrupt. */
+
+ error = acquire_interrupt(this,
+ GPMI_NFC_BCH_INTERRUPT_RES_NAME,
+ gpmi_nfc_bch_isr,
+ &(resources->bch_low_interrupt),
+ &(resources->bch_high_interrupt));
+
+ if (error)
+ goto exit_bch_interrupt;
+
+ /* Attempt to acquire the DMA channels. */
+
+ error = acquire_dma_channels(this,
+ GPMI_NFC_DMA_CHANNELS_RES_NAME,
+ &(resources->dma_low_channel), &(resources->dma_high_channel));
+
+ if (error)
+ goto exit_dma_channels;
+
+ /* Attempt to acquire the DMA interrupt. */
+
+ error = acquire_interrupt(this,
+ GPMI_NFC_DMA_INTERRUPT_RES_NAME,
+ gpmi_nfc_dma_isr,
+ &(resources->dma_low_interrupt),
+ &(resources->dma_high_interrupt));
+
+ if (error)
+ goto exit_dma_interrupt;
+
+ /* Attempt to acquire our clock. */
+
+ error = acquire_clock(this, pdata->clock_name, &(resources->clock));
+
+ if (error)
+ goto exit_clock;
+
+ /* If control arrives here, all went well. */
+
+ return 0;
+
+ /* Control arrives here if something went wrong. */
+
+exit_clock:
+ release_interrupt(this,
+ resources->dma_low_interrupt, resources->dma_high_interrupt);
+exit_dma_interrupt:
+ release_dma_channels(this,
+ resources->dma_low_channel, resources->dma_high_channel);
+exit_dma_channels:
+ release_interrupt(this,
+ resources->bch_low_interrupt, resources->bch_high_interrupt);
+exit_bch_interrupt:
+ release_register_block(this, resources->bch_regs);
+exit_bch_regs:
+ release_register_block(this, resources->gpmi_regs);
+exit_gpmi_regs:
+
+ return error;
+
+}
+
+/**
+ * release_resources() - Releases resources.
+ *
+ * @this: Per-device data.
+ */
+static void release_resources(struct gpmi_nfc_data *this)
+{
+ struct resources *resources = &this->resources;
+
+ release_clock(this, resources->clock);
+ release_register_block(this, resources->gpmi_regs);
+ release_register_block(this, resources->bch_regs);
+ release_interrupt(this,
+ resources->bch_low_interrupt, resources->bch_low_interrupt);
+ release_dma_channels(this,
+ resources->dma_low_channel, resources->dma_high_channel);
+ release_interrupt(this,
+ resources->dma_low_interrupt, resources->dma_high_interrupt);
+}
+
+/**
+ * set_up_nfc_hal() - Sets up the NFC HAL.
+ *
+ * @this: Per-device data.
+ */
+static int set_up_nfc_hal(struct gpmi_nfc_data *this)
+{
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct device *dev = this->dev;
+ struct nfc_hal *nfc;
+ int error = 0;
+ unsigned int i;
+
+ /* Attempt to find an NFC HAL that matches the given version. */
+
+ for (i = 0; i < ARRAY_SIZE(nfc_hals); i++) {
+
+ nfc = nfc_hals[i];
+
+ if (nfc->version == pdata->nfc_version) {
+ this->nfc = nfc;
+ break;
+ }
+
+ }
+
+ /* Check if we found a HAL. */
+
+ if (i >= ARRAY_SIZE(nfc_hals)) {
+ dev_err(dev, "Unkown NFC version %u\n", pdata->nfc_version);
+ return -ENXIO;
+ }
+
+ pr_info("NFC: Version %u, %s\n", nfc->version, nfc->description);
+
+ /*
+ * Check if we can handle the number of chips called for by the platform
+ * data.
+ */
+
+ if (pdata->max_chip_count > nfc->max_chip_count) {
+ dev_err(dev, "Platform data calls for %u chips "
+ "but NFC supports a max of %u.\n",
+ pdata->max_chip_count, nfc->max_chip_count);
+ return -ENXIO;
+ }
+
+ /* Initialize the NFC HAL. */
+
+ error = nfc->init(this);
+
+ if (error)
+ return error;
+
+ /* Set up safe timing. */
+
+ nfc->set_timing(this, &safe_timing);
+
+ /*
+ * If control arrives here, all is well.
+ */
+
+ return 0;
+
+}
+
+/**
+ * set_up_boot_rom_helper() - Sets up the Boot ROM Helper.
+ *
+ * @this: Per-device data.
+ */
+static int set_up_boot_rom_helper(struct gpmi_nfc_data *this)
+{
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct device *dev = this->dev;
+ unsigned int i;
+ struct boot_rom_helper *rom;
+
+ /* Attempt to find a Boot ROM Helper that matches the given version. */
+
+ for (i = 0; i < ARRAY_SIZE(boot_rom_helpers); i++) {
+
+ rom = boot_rom_helpers[i];
+
+ if (rom->version == pdata->boot_rom_version) {
+ this->rom = rom;
+ break;
+ }
+
+ }
+
+ /* Check if we found a Boot ROM Helper. */
+
+ if (i >= ARRAY_SIZE(boot_rom_helpers)) {
+ dev_err(dev, "Unkown Boot ROM version %u\n",
+ pdata->boot_rom_version);
+ return -ENXIO;
+ }
+
+ pr_info("Boot ROM: Version %u, %s\n", rom->version, rom->description);
+
+ /*
+ * If control arrives here, all is well.
+ */
+
+ return 0;
+
+}
+
+/**
+ * manage_sysfs_files() - Creates/removes sysfs files for this device.
+ *
+ * @this: Per-device data.
+ */
+static void manage_sysfs_files(struct gpmi_nfc_data *this, int create)
+{
+ struct device *dev = this->dev;
+ 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(dev, *attr);
+ if (error) {
+ while (--attr >= device_attributes)
+ device_remove_file(dev, *attr);
+ return;
+ }
+ } else {
+ device_remove_file(dev, *attr);
+ }
+
+ }
+
+}
+
+/**
+ * gpmi_nfc_probe() - Probes for a device and, if possible, takes ownership.
+ *
+ * @pdev: A pointer to the platform device data structure.
+ */
+static int gpmi_nfc_probe(struct platform_device *pdev)
+{
+ int error = 0;
+ struct device *dev = &pdev->dev;
+ struct gpmi_nfc_platform_data *pdata = pdev->dev.platform_data;
+ struct gpmi_nfc_data *this = 0;
+
+ /* 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;
+ }
+
+ /* Set up our data structures. */
+
+ platform_set_drvdata(pdev, this);
+
+ this->pdev = pdev;
+ this->dev = &pdev->dev;
+ this->pdata = pdata;
+
+ /* Acquire the resources we need. */
+
+ error = acquire_resources(this);
+
+ if (error)
+ goto exit_acquire_resources;
+
+ /* Set up the NFC. */
+
+ error = set_up_nfc_hal(this);
+
+ if (error)
+ goto exit_nfc_init;
+
+ /* Set up the platform. */
+
+ if (pdata->platform_init)
+ error = pdata->platform_init(pdata->max_chip_count);
+
+ if (error)
+ goto exit_platform_init;
+
+ /* Set up the Boot ROM Helper. */
+
+ error = set_up_boot_rom_helper(this);
+
+ if (error)
+ goto exit_boot_rom_helper_init;
+
+ /* Initialize the MTD Interface Layer. */
+
+ error = gpmi_nfc_mil_init(this);
+
+ if (error)
+ goto exit_mil_init;
+
+ /* Create sysfs entries for this device. */
+
+ manage_sysfs_files(this, true);
+
+ /* Return success. */
+
+ return 0;
+
+ /* Error return paths begin here. */
+
+exit_mil_init:
+exit_boot_rom_helper_init:
+ if (pdata->platform_exit)
+ pdata->platform_exit(pdata->max_chip_count);
+exit_platform_init:
+ this->nfc->exit(this);
+exit_nfc_init:
+ release_resources(this);
+exit_acquire_resources:
+ platform_set_drvdata(pdev, NULL);
+ kfree(this);
+exit_allocate_this:
+exit_validate_platform:
+ return error;
+
+}
+
+/**
+ * gpmi_nfc_remove() - Dissociates this driver from the given device.
+ *
+ * @pdev: A pointer to the platform device data structure.
+ */
+static int __exit gpmi_nfc_remove(struct platform_device *pdev)
+{
+ struct gpmi_nfc_data *this = platform_get_drvdata(pdev);
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+
+ manage_sysfs_files(this, false);
+ gpmi_nfc_mil_exit(this);
+ if (pdata->platform_exit)
+ pdata->platform_exit(pdata->max_chip_count);
+ this->nfc->exit(this);
+ release_resources(this);
+ platform_set_drvdata(pdev, NULL);
+ kfree(this);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/**
+ * gpmi_nfc_suspend() - Puts the NFC into a low power state.
+ *
+ * @pdev: A pointer to the platform device data structure.
+ * @state: The new power state.
+ */
+static int gpmi_nfc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+/**
+ * gpmi_nfc_resume() - Brings the NFC back from a low power state.
+ *
+ * @pdev: A pointer to the platform device data structure.
+ */
+static int gpmi_nfc_resume(struct platform_device *pdev)
+{
+ 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 gpmi_nfc_driver = {
+ .driver = {
+ .name = GPMI_NFC_DRIVER_NAME,
+ },
+ .probe = gpmi_nfc_probe,
+ .remove = __exit_p(gpmi_nfc_remove),
+ .suspend = gpmi_nfc_suspend,
+ .resume = gpmi_nfc_resume,
+};
+
+/**
+ * gpmi_nfc_init() - Initializes this module.
+ */
+static int __init gpmi_nfc_init(void)
+{
+
+ pr_info("i.MX GPMI NFC\n");
+
+#ifdef CONFIG_ARCH_MX50
+ if (enable_gpmi_nand == 0)
+ return 0;
+#endif
+
+ /* Register this driver with the platform management system. */
+
+ if (platform_driver_register(&gpmi_nfc_driver) != 0) {
+ pr_err("i.MX GPMI NFC driver registration failed\n");
+ return -ENODEV;
+ }
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * gpmi_nfc_exit() - Deactivates this module.
+ */
+static void __exit gpmi_nfc_exit(void)
+{
+ platform_driver_unregister(&gpmi_nfc_driver);
+}
+
+static int __init gpmi_nand_setup(char *__unused)
+{
+ enable_gpmi_nand = 1;
+ return 1;
+}
+__setup("gpmi:nand", gpmi_nand_setup);
+
+module_init(gpmi_nfc_init);
+module_exit(gpmi_nfc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c
new file mode 100644
index 000000000000..efb0f93c234e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-mil.c
@@ -0,0 +1,1929 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include "gpmi-nfc.h"
+#include "linux/slab.h"
+
+/*
+ * Indicates the driver should register the MTD that represents the entire
+ * medium, thus making it visible.
+ */
+
+static int register_main_mtd;
+module_param(register_main_mtd, int, 0400);
+
+/*
+ * Indicates the driver should attempt to perform DMA directly to/from buffers
+ * passed into this driver. This is true by default. If false, the driver will
+ * *always* copy incoming/outgoing data to/from its own DMA buffers.
+ */
+
+static int map_io_buffers = true;
+module_param(map_io_buffers, int, 0600);
+
+/* add our owner bbt descriptor */
+static uint8_t scan_ff_pattern[] = { 0xff };
+static struct nand_bbt_descr gpmi_bbt_descr = {
+ .options = 0,
+ .offs = 0,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+/**
+ * mil_outgoing_buffer_dma_begin() - Begins DMA on an outgoing buffer.
+ *
+ * @this: Per-device data.
+ * @source: The source buffer.
+ * @length: The length of the data in the source buffer.
+ * @alt_virt: The virtual address of an alternate buffer which is ready to be
+ * used for DMA.
+ * @alt_phys: The physical address of an alternate buffer which is ready to be
+ * used for DMA.
+ * @alt_size: The size of the alternate buffer.
+ * @use_virt: A pointer to a variable that will receive the virtual address to
+ * use.
+ * @use_phys: A pointer to a variable that will receive the physical address to
+ * use.
+ */
+static int mil_outgoing_buffer_dma_begin(struct gpmi_nfc_data *this,
+ const void *source, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ const void **use_virt, dma_addr_t *use_phys)
+{
+ struct device *dev = this->dev;
+ dma_addr_t source_phys = ~0;
+
+ /*
+ * If we can, we want to use the caller's buffer directly for DMA. Check
+ * if the system will let us map them.
+ */
+ if (map_io_buffers && virt_addr_valid(source))
+ source_phys =
+ dma_map_single(dev,
+ (void *) source, length, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, source_phys)) {
+ /*
+ * If control arrives here, we're not mapping the source buffer.
+ * Make sure the alternate is large enough.
+ */
+ if (alt_size < length) {
+ dev_err(dev, "Alternate buffer is too small "
+ "for outgoing I/O\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Copy the contents of the source buffer into the alternate
+ * buffer and set up the return values accordingly.
+ */
+ memcpy(alt_virt, source, length);
+
+ *use_virt = alt_virt;
+ *use_phys = alt_phys;
+ } else {
+ /*
+ * If control arrives here, we're mapping the source buffer. Set
+ * up the return values accordingly.
+ */
+ *use_virt = source;
+ *use_phys = source_phys;
+ }
+ return 0;
+}
+
+/**
+ * mil_outgoing_buffer_dma_end() - Ends DMA on an outgoing buffer.
+ *
+ * @this: Per-device data.
+ * @source: The source buffer.
+ * @length: The length of the data in the source buffer.
+ * @alt_virt: The virtual address of an alternate buffer which was ready to be
+ * used for DMA.
+ * @alt_phys: The physical address of an alternate buffer which was ready to
+ * be used for DMA.
+ * @alt_size: The size of the alternate buffer.
+ * @used_virt: The virtual address that was used.
+ * @used_phys: The physical address that was used.
+ */
+static void mil_outgoing_buffer_dma_end(struct gpmi_nfc_data *this,
+ const void *source, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ const void *used_virt, dma_addr_t used_phys)
+{
+ struct device *dev = this->dev;
+
+ /*
+ * Check if we used the source buffer, and it's not one of our own DMA
+ * buffers. If so, we need to unmap it.
+ */
+ if (used_virt == source)
+ dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE);
+}
+
+/**
+ * mil_incoming_buffer_dma_begin() - Begins DMA on an incoming buffer.
+ *
+ * @this: Per-device data.
+ * @destination: The destination buffer.
+ * @length: The length of the data that will arrive.
+ * @alt_virt: The virtual address of an alternate buffer which is ready
+ * to be used for DMA.
+ * @alt_phys: The physical address of an alternate buffer which is ready
+ * to be used for DMA.
+ * @alt_size: The size of the alternate buffer.
+ * @use_virt: A pointer to a variable that will receive the virtual address
+ * to use.
+ * @use_phys: A pointer to a variable that will receive the physical address
+ * to use.
+ */
+static int mil_incoming_buffer_dma_begin(struct gpmi_nfc_data *this,
+ void *destination, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ void **use_virt, dma_addr_t *use_phys)
+{
+ struct device *dev = this->dev;
+ dma_addr_t destination_phys = ~0;
+
+ /*
+ * If we can, we want to use the caller's buffer directly for DMA. Check
+ * if the system will let us map them.
+ */
+ if (map_io_buffers && virt_addr_valid(destination) &&
+ !((int)destination & 0x3) && 0)
+ destination_phys =
+ dma_map_single(dev,
+ (void *) destination, length, DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, destination_phys)) {
+ /*
+ * If control arrives here, we're not mapping the destination
+ * buffer. Make sure the alternate is large enough.
+ */
+ if (alt_size < length) {
+ dev_err(dev, "Alternate buffer is too small "
+ "for incoming I/O\n");
+ return -ENOMEM;
+ }
+
+ /* Set up the return values to use the alternate. */
+ *use_virt = alt_virt;
+ *use_phys = alt_phys;
+ } else {
+ /*
+ * If control arrives here, we're mapping the destination
+ * buffer. Set up the return values accordingly.
+ */
+ *use_virt = destination;
+ *use_phys = destination_phys;
+ }
+ return 0;
+}
+
+/**
+ * mil_incoming_buffer_dma_end() - Ends DMA on an incoming buffer.
+ *
+ * @this: Per-device data.
+ * @destination: The destination buffer.
+ * @length: The length of the data that arrived.
+ * @alt_virt: The virtual address of an alternate buffer which was ready to
+ * be used for DMA.
+ * @alt_phys: The physical address of an alternate buffer which was ready to
+ * be used for DMA.
+ * @alt_size: The size of the alternate buffer.
+ * @used_virt: The virtual address that was used.
+ * @used_phys: The physical address that was used.
+ */
+static void mil_incoming_buffer_dma_end(struct gpmi_nfc_data *this,
+ void *destination, unsigned length,
+ void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
+ void *used_virt, dma_addr_t used_phys)
+{
+ struct device *dev = this->dev;
+
+ /*
+ * Check if we used the destination buffer, and it's not one of our own
+ * DMA buffers. If so, we need to unmap it.
+ */
+ if (used_virt == destination)
+ dma_unmap_single(dev, used_phys, length, DMA_FROM_DEVICE);
+ else
+ memcpy(destination, alt_virt, length);
+}
+
+/* Free all the DMA buffers */
+static void mil_free_dma_buffer(struct gpmi_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct mil *mil = &this->mil;
+
+ /* Free the DMA buffer, if it has been allocated. */
+ if (mil->page_buffer_virt && virt_addr_valid(mil->page_buffer_virt))
+ dma_free_coherent(dev, mil->page_buffer_size,
+ mil->page_buffer_virt,
+ mil->page_buffer_phys);
+ mil->page_buffer_size = 0;
+ mil->page_buffer_virt = 0;
+ mil->page_buffer_phys = ~0;
+
+ /* Free the command buffer, if it's been allocated. */
+ if (mil->cmd_virt)
+ dma_free_coherent(dev, MIL_COMMAND_BUFFER_SIZE,
+ mil->cmd_virt, mil->cmd_phys);
+ mil->cmd_virt = 0;
+ mil->cmd_phys = ~0;
+}
+
+/* Allocate the DMA command buffer and DMA page buffer */
+static int mil_alloc_dma_buffer(struct gpmi_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mil *mil = &this->mil;
+
+ /* Allocate a command buffer. */
+ mil->cmd_virt = dma_alloc_coherent(dev, MIL_COMMAND_BUFFER_SIZE,
+ &mil->cmd_phys, GFP_DMA);
+ if (!mil->cmd_virt)
+ goto error_alloc;
+
+ /*
+ * Allocate the page buffer.
+ *
+ * Both the payload buffer and the auxiliary buffer must appear on
+ * 32-bit boundaries. We presume the size of the payload buffer is a
+ * power of two and is much larger than four, which guarantees the
+ * auxiliary buffer will appear on a 32-bit boundary.
+ */
+ mil->page_buffer_size = nfc_geo->payload_size_in_bytes +
+ nfc_geo->auxiliary_size_in_bytes;
+ mil->page_buffer_virt = dma_alloc_coherent(dev, mil->page_buffer_size,
+ &mil->page_buffer_phys, GFP_DMA);
+ if (!mil->page_buffer_virt)
+ goto error_alloc;
+
+ /* Slice up the page buffer. */
+ mil->payload_virt = mil->page_buffer_virt;
+ mil->payload_phys = mil->page_buffer_phys;
+ mil->auxiliary_virt = ((char *) mil->payload_virt) +
+ nfc_geo->payload_size_in_bytes;
+ mil->auxiliary_phys = mil->payload_phys +
+ nfc_geo->payload_size_in_bytes;
+ return 0;
+
+error_alloc:
+ printk(KERN_ERR "allocate DMA buffer error!!\n");
+ mil_free_dma_buffer(this);
+ dump_stack();
+ return -ENOMEM;
+}
+
+/**
+ * mil_cmd_ctrl - MTD Interface cmd_ctrl()
+ *
+ * This is the function that we install in the cmd_ctrl function pointer of the
+ * owning struct nand_chip. The only functions in the reference implementation
+ * that use these functions pointers are cmdfunc and select_chip.
+ *
+ * In this driver, we implement our own select_chip, so this function will only
+ * be called by the reference implementation's cmdfunc. For this reason, we can
+ * ignore the chip enable bit and concentrate only on sending bytes to the
+ * NAND Flash.
+ *
+ * @mtd: The owning MTD.
+ * @data: The value to push onto the data signals.
+ * @ctrl: The values to push onto the control signals.
+ */
+static void mil_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct gpmi_nfc_data *this = nand->priv;
+ struct device *dev = this->dev;
+ struct mil *mil = &this->mil;
+ struct nfc_hal *nfc = this->nfc;
+ int error;
+#if defined(CONFIG_MTD_DEBUG)
+ unsigned int i;
+ char display[MIL_COMMAND_BUFFER_SIZE * 5];
+#endif
+
+ /*
+ * Every operation begins with a command byte and a series of zero or
+ * more address bytes. These are distinguished by either the Address
+ * Latch Enable (ALE) or Command Latch Enable (CLE) signals being
+ * asserted. When MTD is ready to execute the command, it will deassert
+ * both latch enables.
+ *
+ * Rather than run a separate DMA operation for every single byte, we
+ * queue them up and run a single DMA operation for the entire series
+ * of command and data bytes.
+ */
+ if ((ctrl & (NAND_ALE | NAND_CLE))) {
+ if (data != NAND_CMD_NONE)
+ mil->cmd_virt[mil->command_length++] = data;
+ return;
+ }
+
+ /*
+ * If control arrives here, MTD has deasserted both the ALE and CLE,
+ * which means it's ready to run an operation. Check if we have any
+ * bytes to send.
+ */
+ if (!mil->command_length)
+ return;
+
+ /* Hand the command over to the NFC. */
+#if defined(CONFIG_MTD_DEBUG)
+ display[0] = 0;
+ for (i = 0; i < mil->command_length; i++)
+ sprintf(display + strlen(display), " 0x%02x",
+ mil->cmd_virt[i] & 0xff);
+ DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc cmd_ctrl] command: %s\n", display);
+#endif
+
+ error = nfc->send_command(this,
+ mil->current_chip, mil->cmd_phys, mil->command_length);
+ if (error) {
+ dev_err(dev, "[%s] Chip: %u, Error %d\n",
+ __func__, mil->current_chip, error);
+ print_hex_dump(KERN_ERR,
+ " Command Bytes: ", DUMP_PREFIX_NONE, 16, 1,
+ mil->cmd_virt, mil->command_length, 0);
+ }
+
+ /* Reset. */
+ mil->command_length = 0;
+}
+
+/**
+ * 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 gpmi_nfc_data *this = nand->priv;
+ struct nfc_hal *nfc = this->nfc;
+ struct mil *mil = &this->mil;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc dev_ready]\n");
+
+ return nfc->is_ready(this, mil->current_chip);
+}
+
+/**
+ * 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 gpmi_nfc_data *this = nand->priv;
+ struct mil *mil = &this->mil;
+ struct nfc_hal *nfc = this->nfc;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc select_chip] chip: %d\n", chip);
+
+ /* Figure out what kind of transition this is. */
+ if ((mil->current_chip < 0) && (chip >= 0)) {
+ nfc->begin(this);
+ } else if ((mil->current_chip >= 0) && (chip < 0)) {
+ nfc->end(this);
+ } else {
+ }
+
+ mil->current_chip = chip;
+}
+
+/**
+ * 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 gpmi_nfc_data *this = nand->priv;
+ struct device *dev = this->dev;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mil *mil = &this->mil;
+ void *use_virt = 0;
+ dma_addr_t use_phys = ~0;
+ int error;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc readbuf] len: %d\n", len);
+
+ /* Set up DMA. */
+ error = mil_incoming_buffer_dma_begin(this, buf, len,
+ mil->payload_virt, mil->payload_phys,
+ nfc_geo->payload_size_in_bytes,
+ &use_virt, &use_phys);
+ if (error) {
+ dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__);
+ return;
+ }
+
+ /* Ask the NFC. */
+ nfc->read_data(this, mil->current_chip, use_phys, len);
+
+ /* Finish with DMA. */
+ mil_incoming_buffer_dma_end(this, buf, len,
+ mil->payload_virt, mil->payload_phys,
+ nfc_geo->payload_size_in_bytes,
+ use_virt, use_phys);
+}
+
+/**
+ * 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 gpmi_nfc_data *this = nand->priv;
+ struct device *dev = this->dev;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mil *mil = &this->mil;
+ const void *use_virt = 0;
+ dma_addr_t use_phys = ~0;
+ int error;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc writebuf] len: %d\n", len);
+ /* Set up DMA. */
+ error = mil_outgoing_buffer_dma_begin(this, buf, len,
+ mil->payload_virt, mil->payload_phys,
+ nfc_geo->payload_size_in_bytes,
+ &use_virt, &use_phys);
+ if (error) {
+ dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__);
+ return;
+ }
+
+ /* Ask the NFC. */
+ nfc->send_data(this, mil->current_chip, use_phys, len);
+
+ /* Finish with DMA. */
+ mil_outgoing_buffer_dma_end(this, buf, len,
+ mil->payload_virt, mil->payload_phys,
+ nfc_geo->payload_size_in_bytes,
+ use_virt, use_phys);
+}
+
+/**
+ * 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;
+
+ mil_read_buf(mtd, (uint8_t *)&byte, 1);
+ return byte;
+}
+
+/**
+ * mil_handle_block_mark_swapping() - Handles block mark swapping.
+ *
+ * Note that, when this function is called, it doesn't know whether it's
+ * swapping the block mark, or swapping it *back* -- but it doesn't matter
+ * because the the operation is the same.
+ *
+ * @this: Per-device data.
+ * @payload: A pointer to the payload buffer.
+ * @auxiliary: A pointer to the auxiliary buffer.
+ */
+static void mil_handle_block_mark_swapping(struct gpmi_nfc_data *this,
+ void *payload, void *auxiliary)
+{
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct boot_rom_helper *rom = this->rom;
+ unsigned char *p;
+ unsigned char *a;
+ unsigned int bit;
+ unsigned char mask;
+ unsigned char from_data;
+ unsigned char from_oob;
+
+ /* Check if we're doing block mark swapping. */
+ if (!rom->swap_block_mark)
+ return;
+
+ /*
+ * If control arrives here, we're swapping. Make some convenience
+ * variables.
+ */
+ bit = nfc_geo->block_mark_bit_offset;
+ p = ((unsigned char *) payload) + nfc_geo->block_mark_byte_offset;
+ a = auxiliary;
+
+ /*
+ * Get the byte from the data area that overlays the block mark. Since
+ * the ECC engine applies its own view to the bits in the page, the
+ * physical block mark won't (in general) appear on a byte boundary in
+ * the data.
+ */
+ from_data = (p[0] >> bit) | (p[1] << (8 - bit));
+
+ /* Get the byte from the OOB. */
+ from_oob = a[0];
+
+ /* Swap them. */
+ a[0] = from_data;
+
+ mask = (0x1 << bit) - 1;
+ p[0] = (p[0] & mask) | (from_oob << bit);
+
+ mask = ~0 << bit;
+ p[1] = (p[1] & mask) | (from_oob >> (8 - bit));
+}
+
+/**
+ * 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 page)
+{
+ struct gpmi_nfc_data *this = nand->priv;
+ struct device *dev = this->dev;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct mil *mil = &this->mil;
+ void *payload_virt = 0;
+ dma_addr_t payload_phys = ~0;
+ void *auxiliary_virt = 0;
+ dma_addr_t auxiliary_phys = ~0;
+ unsigned int i;
+ unsigned char *status;
+ unsigned int failed;
+ unsigned int corrected;
+ int error = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_read_page]\n");
+
+ /*
+ * Set up DMA.
+ *
+ * Notice that we don't try to use the caller's buffer as the auxiliary.
+ * We need to do a lot of fiddling to deliver the OOB, so there's no
+ * point.
+ */
+ error = mil_incoming_buffer_dma_begin(this, buf, mtd->writesize,
+ mil->payload_virt, mil->payload_phys,
+ nfc_geo->payload_size_in_bytes,
+ &payload_virt, &payload_phys);
+ if (error) {
+ dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__);
+ error = -ENOMEM;
+ goto exit_payload;
+ }
+ auxiliary_virt = mil->auxiliary_virt;
+ auxiliary_phys = mil->auxiliary_phys;
+
+ /* Ask the NFC. */
+ error = nfc->read_page(this, mil->current_chip,
+ payload_phys, auxiliary_phys);
+ if (error) {
+ dev_err(dev, "[%s] Error in ECC-based read: %d\n",
+ __func__, error);
+ goto exit_nfc;
+ }
+
+ /* Handle block mark swapping. */
+ mil_handle_block_mark_swapping(this, payload_virt, auxiliary_virt);
+
+ /* Loop over status bytes, accumulating ECC status. */
+ failed = 0;
+ corrected = 0;
+ status = ((unsigned char *) auxiliary_virt) +
+ nfc_geo->auxiliary_status_offset;
+
+ for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
+ if ((*status == 0x00) || (*status == 0xff))
+ continue;
+
+ if (*status == 0xfe) {
+ failed++;
+ continue;
+ }
+ corrected += *status;
+ }
+
+ /*
+ * Propagate ECC status to the owning MTD only when failed or
+ * corrected times nearly reaches our ECC correction threshold.
+ */
+ if (failed || corrected >= (nfc_geo->ecc_strength - 1)) {
+ mtd->ecc_stats.failed += failed;
+ mtd->ecc_stats.corrected += corrected;
+ }
+
+ /*
+ * It's time to deliver the OOB bytes. See mil_ecc_read_oob() for
+ * details about our policy for delivering the OOB.
+ *
+ * We fill the caller's buffer with set bits, and then copy the block
+ * mark to th caller's buffer. Note that, if block mark swapping was
+ * necessary, it has already been done, so we can rely on the first
+ * byte of the auxiliary buffer to contain the block mark.
+ */
+ memset(nand->oob_poi, ~0, mtd->oobsize);
+ nand->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
+
+exit_nfc:
+ mil_incoming_buffer_dma_end(this, buf, mtd->writesize,
+ mil->payload_virt, mil->payload_phys,
+ nfc_geo->payload_size_in_bytes,
+ payload_virt, payload_phys);
+exit_payload:
+ return error;
+}
+
+/**
+ * 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 gpmi_nfc_data *this = nand->priv;
+ struct device *dev = this->dev;
+ struct nfc_hal *nfc = this->nfc;
+ struct nfc_geometry *nfc_geo = &this->nfc_geometry;
+ struct boot_rom_helper *rom = this->rom;
+ struct mil *mil = &this->mil;
+ const void *payload_virt = 0;
+ dma_addr_t payload_phys = ~0;
+ const void *auxiliary_virt = 0;
+ dma_addr_t auxiliary_phys = ~0;
+ int error;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_write_page]\n");
+
+ /* Set up DMA. */
+ if (rom->swap_block_mark) {
+ /*
+ * If control arrives here, we're doing block mark swapping.
+ * Since we can't modify the caller's buffers, we must copy them
+ * into our own.
+ */
+ memcpy(mil->payload_virt, buf, mtd->writesize);
+ payload_virt = mil->payload_virt;
+ payload_phys = mil->payload_phys;
+
+ memcpy(mil->auxiliary_virt, nand->oob_poi,
+ nfc_geo->auxiliary_size_in_bytes);
+ auxiliary_virt = mil->auxiliary_virt;
+ auxiliary_phys = mil->auxiliary_phys;
+
+ /* Handle block mark swapping. */
+ mil_handle_block_mark_swapping(this,
+ (void *) payload_virt, (void *) auxiliary_virt);
+ } else {
+ /*
+ * If control arrives here, we're not doing block mark swapping,
+ * so we can to try and use the caller's buffers.
+ */
+ error = mil_outgoing_buffer_dma_begin(this,
+ buf, mtd->writesize,
+ mil->payload_virt, mil->payload_phys,
+ nfc_geo->payload_size_in_bytes,
+ &payload_virt, &payload_phys);
+ if (error) {
+ dev_err(dev, "[%s] Inadequate payload DMA buffer\n",
+ __func__);
+ return;
+ }
+
+ error = mil_outgoing_buffer_dma_begin(this,
+ nand->oob_poi, mtd->oobsize,
+ mil->auxiliary_virt, mil->auxiliary_phys,
+ nfc_geo->auxiliary_size_in_bytes,
+ &auxiliary_virt, &auxiliary_phys);
+ if (error) {
+ dev_err(dev, "[%s] Inadequate auxiliary DMA buffer\n",
+ __func__);
+ goto exit_auxiliary;
+ }
+ }
+
+ /* Ask the NFC. */
+ error = nfc->send_page(this, mil->current_chip,
+ payload_phys, auxiliary_phys);
+ if (error)
+ dev_err(dev, "[%s] Error in ECC-based write: %d\n",
+ __func__, error);
+
+ /* Return. */
+ if (!rom->swap_block_mark)
+ mil_outgoing_buffer_dma_end(this, nand->oob_poi, mtd->oobsize,
+ mil->auxiliary_virt, mil->auxiliary_phys,
+ nfc_geo->auxiliary_size_in_bytes,
+ auxiliary_virt, auxiliary_phys);
+exit_auxiliary:
+ if (!rom->swap_block_mark)
+ mil_outgoing_buffer_dma_end(this, buf, mtd->writesize,
+ mil->payload_virt, mil->payload_phys,
+ nfc_geo->payload_size_in_bytes,
+ payload_virt, payload_phys);
+}
+
+/**
+ * mil_hook_block_markbad() - Hooked MTD Interface block_markbad().
+ *
+ * This function is a veneer that replaces the function originally installed by
+ * the NAND Flash MTD code. See the description of the marking_a_bad_block field
+ * in struct mil for more information about this.
+ *
+ * @mtd: A pointer to the MTD.
+ * @ofs: Byte address of the block to mark.
+ */
+static int mil_hook_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct gpmi_nfc_data *this = chip->priv;
+ struct mil *mil = &this->mil;
+ int ret;
+
+ mil->marking_a_bad_block = true;
+ ret = mil->hooked_block_markbad(mtd, ofs);
+ mil->marking_a_bad_block = false;
+ return ret;
+}
+
+/**
+ * mil_ecc_read_oob() - MTD Interface ecc.read_oob().
+ *
+ * There are several places in this driver where we have to handle the OOB and
+ * block marks. This is the function where things are the most complicated, so
+ * this is where we try to explain it all. All the other places refer back to
+ * here.
+ *
+ * These are the rules, in order of decreasing importance:
+ *
+ * 1) Nothing the caller does can be allowed to imperil the block mark, so all
+ * write operations take measures to protect it.
+ *
+ * 2) In read operations, the first byte of the OOB we return must reflect the
+ * true state of the block mark, no matter where that block mark appears in
+ * the physical page.
+ *
+ * 3) ECC-based read operations return an OOB full of set bits (since we never
+ * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads
+ * return).
+ *
+ * 4) "Raw" read operations return a direct view of the physical bytes in the
+ * page, using the conventional definition of which bytes are data and which
+ * are OOB. This gives the caller a way to see the actual, physical bytes
+ * in the page, without the distortions applied by our ECC engine.
+ *
+ *
+ * What we do for this specific read operation depends on two questions:
+ *
+ * 1) Are we doing a "raw" read, or an ECC-based read?
+ *
+ * 2) Are we using block mark swapping or transcription?
+ *
+ * There are four cases, illustrated by the following Karnaugh map:
+ *
+ * | Raw | ECC-based |
+ * -------------+-------------------------+-------------------------+
+ * | Read the conventional | |
+ * | OOB at the end of the | |
+ * Swapping | page and return it. It | |
+ * | contains exactly what | |
+ * | we want. | Read the block mark and |
+ * -------------+-------------------------+ return it in a buffer |
+ * | Read the conventional | full of set bits. |
+ * | OOB at the end of the | |
+ * | page and also the block | |
+ * Transcribing | mark in the metadata. | |
+ * | Copy the block mark | |
+ * | into the first byte of | |
+ * | the OOB. | |
+ * -------------+-------------------------+-------------------------+
+ *
+ * Note that we break rule #4 in the Transcribing/Raw case because we're not
+ * giving an accurate view of the actual, physical bytes in the page (we're
+ * overwriting the block mark). That's OK because it's more important to follow
+ * rule #2.
+ *
+ * It turns out that knowing whether we want an "ECC-based" or "raw" read is not
+ * easy. When reading a page, for example, the NAND Flash MTD code calls our
+ * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an
+ * ECC-based or raw view of the page is implicit in which function it calls
+ * (there is a similar pair of ECC-based/raw functions for writing).
+ *
+ * Since MTD assumes the OOB is not covered by ECC, there is no pair of
+ * ECC-based/raw functions for reading or or writing the OOB. The fact that the
+ * caller wants an ECC-based or raw view of the page is not propagated down to
+ * this driver.
+ *
+ * @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 gpmi_nfc_data *this = nand->priv;
+ struct boot_rom_helper *rom = this->rom;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_read_oob] "
+ "page: 0x%06x, sndcmd: %s\n", page, sndcmd ? "Yes" : "No");
+
+ /* clear the OOB buffer */
+ memset(nand->oob_poi, ~0, mtd->oobsize);
+
+ /* Read out the conventional OOB. */
+ nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ nand->read_buf(mtd, nand->oob_poi, mtd->oobsize);
+
+ /*
+ * Now, we want to make sure the block mark is correct. In the
+ * Swapping/Raw case, we already have it. Otherwise, we need to
+ * explicitly read it.
+ */
+ if (!rom->swap_block_mark) {
+ /* Read the block mark into the first byte of the OOB buffer. */
+ nand->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ nand->oob_poi[0] = nand->read_byte(mtd);
+ }
+
+ /*
+ * Return true, indicating that the next call to this function must send
+ * a command.
+ */
+ return true;
+}
+
+/**
+ * mil_ecc_write_oob() - MTD Interface ecc.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 gpmi_nfc_data *this = nand->priv;
+ struct device *dev = this->dev;
+ struct mil *mil = &this->mil;
+ struct boot_rom_helper *rom = this->rom;
+ uint8_t block_mark = 0;
+ int block_mark_column;
+ int status;
+ int error = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "[gpmi_nfc ecc_write_oob] page: 0x%06x\n", page);
+ /*
+ * There are fundamental incompatibilities between the i.MX GPMI NFC and
+ * the NAND Flash MTD model that make it essentially impossible to write
+ * the out-of-band bytes.
+ *
+ * We permit *ONE* exception. If the *intent* of writing the OOB is to
+ * mark a block bad, we can do that.
+ */
+ if (!mil->marking_a_bad_block) {
+ dev_emerg(dev, "This driver doesn't support writing the OOB\n");
+ WARN_ON(1);
+ error = -EIO;
+ goto exit;
+ }
+
+ /*
+ * If control arrives here, we're marking a block bad. First, figure out
+ * where the block mark is.
+ *
+ * If we're using swapping, the block mark is in the conventional
+ * location. Otherwise, we're using transcription, and the block mark
+ * appears in the first byte of the page.
+ */
+ if (rom->swap_block_mark)
+ block_mark_column = mtd->writesize;
+ else
+ block_mark_column = 0;
+
+ /* Write the block mark. */
+ nand->cmdfunc(mtd, NAND_CMD_SEQIN, block_mark_column, page);
+ nand->write_buf(mtd, &block_mark, 1);
+ nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ status = nand->waitfunc(mtd, nand);
+
+ /* Check if it worked. */
+ if (status & NAND_STATUS_FAIL)
+ error = -EIO;
+exit:
+ return error;
+}
+
+/**
+ * mil_block_bad - Claims all blocks are good.
+ *
+ * In principle, this function is *only* called when the NAND Flash MTD system
+ * isn't allowed to keep an in-memory bad block table, so it is forced to ask
+ * the driver for bad block information.
+ *
+ * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so
+ * this function is *only* called when we take it away.
+ *
+ * We take away the in-memory BBT when the user sets the "ignorebad" parameter,
+ * which indicates that all blocks should be reported good.
+ *
+ * Thus, this function is only called when we want *all* blocks to look good,
+ * so it *always* return success.
+ *
+ * @mtd: Ignored.
+ * @ofs: Ignored.
+ * @getchip: Ignored.
+ */
+static int mil_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ return 0;
+}
+
+
+/**
+ * mil_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 mil_set_nfc_geometry(struct gpmi_nfc_data *this)
+{
+ struct nfc_hal *nfc = this->nfc;
+#if defined(DETAILED_INFO)
+ struct nfc_geometry *geo = &this->nfc_geometry;
+#endif
+ /* Set the NFC geometry. */
+ if (nfc->set_geometry(this))
+ return !0;
+
+ #if defined(DETAILED_INFO)
+ pr_info("------------\n");
+ pr_info("NFC Geometry\n");
+ pr_info("------------\n");
+ pr_info("ECC Algorithm : %s\n", geo->ecc_algorithm);
+ pr_info("ECC Strength : %u\n", geo->ecc_strength);
+ pr_info("Page Size in Bytes : %u\n", geo->page_size_in_bytes);
+ pr_info("Metadata Size in Bytes : %u\n", geo->metadata_size_in_bytes);
+ pr_info("ECC Chunk Size in Bytes: %u\n", geo->ecc_chunk_size_in_bytes);
+ pr_info("ECC Chunk Count : %u\n", geo->ecc_chunk_count);
+ pr_info("Payload Size in Bytes : %u\n", geo->payload_size_in_bytes);
+ pr_info("Auxiliary Size in Bytes: %u\n", geo->auxiliary_size_in_bytes);
+ pr_info("Auxiliary Status Offset: %u\n", geo->auxiliary_status_offset);
+ pr_info("Block Mark Byte Offset : %u\n", geo->block_mark_byte_offset);
+ pr_info("Block Mark Bit Offset : %u\n", geo->block_mark_bit_offset);
+ #endif
+
+ return 0;
+}
+
+/**
+ * mil_set_boot_rom_helper_geometry() - Set up the Boot ROM Helper geometry.
+ *
+ * @this: Per-device data.
+ */
+static int mil_set_boot_rom_helper_geometry(struct gpmi_nfc_data *this)
+{
+ struct boot_rom_helper *rom = this->rom;
+#if defined(DETAILED_INFO)
+ struct boot_rom_geometry *geo = &this->rom_geometry;
+#endif
+
+ /* Set the Boot ROM Helper geometry. */
+ if (rom->set_geometry(this))
+ return !0;
+
+ #if defined(DETAILED_INFO)
+ pr_info("-----------------\n");
+ pr_info("Boot ROM Geometry\n");
+ pr_info("-----------------\n");
+ pr_info("Boot Area Count : %u\n", geo->boot_area_count);
+ pr_info("Boot Area Size in Bytes : %u (0x%x)\n",
+ geo->boot_area_size_in_bytes, geo->boot_area_size_in_bytes);
+ pr_info("Stride Size in Pages : %u\n", geo->stride_size_in_pages);
+ pr_info("Search Area Stride Exponent: %u\n",
+ geo->search_area_stride_exponent);
+ #endif
+ return 0;
+}
+
+/**
+ * mil_set_geometry() - Set up the medium geometry.
+ *
+ * @this: Per-device data.
+ */
+static int mil_set_geometry(struct gpmi_nfc_data *this)
+{
+ /* Free the temporary DMA memory for read ID case */
+ mil_free_dma_buffer(this);
+
+ /* Set up the NFC geometry. */
+ if (mil_set_nfc_geometry(this))
+ return -ENXIO;
+
+ /* Alloc the new DMA buffers according to the pagesize and oobsize */
+ return mil_alloc_dma_buffer(this);
+}
+
+/**
+ * mil_pre_bbt_scan() - Prepare for the BBT scan.
+ *
+ * @this: Per-device data.
+ */
+static int mil_pre_bbt_scan(struct gpmi_nfc_data *this)
+{
+ struct boot_rom_helper *rom = this->rom;
+ int error = 0;
+
+ if (mil_set_boot_rom_helper_geometry(this))
+ return -ENXIO;
+
+ /* This is ROM arch-specific initilization before the BBT scanning. */
+ if (rom->rom_extra_init)
+ error = rom->rom_extra_init(this);
+ return error;
+}
+
+/**
+ * mil_scan_bbt() - MTD Interface scan_bbt().
+ *
+ * The HIL calls this function once, when it initializes the NAND Flash MTD.
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static int mil_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct gpmi_nfc_data *this = nand->priv;
+ int error;
+
+ /*
+ * 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;
+
+ /* Prepare for the BBT scan. */
+ error = mil_pre_bbt_scan(this);
+ if (error)
+ return error;
+
+ /* use the default BBT implementation */
+ return nand_default_bbt(mtd);
+}
+
+/**
+ * mil_boot_areas_init() - Initializes boot areas.
+ *
+ * @this: Per-device data.
+ */
+static int mil_boot_areas_init(struct gpmi_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct boot_rom_geometry *rom = &this->rom_geometry;
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+ struct nand_chip *nand = &mil->nand;
+ struct nand_device_info *info = &this->device_info;
+ int mtd_support_is_adequate;
+ unsigned int i;
+ struct mtd_partition partitions[4];
+ struct mtd_info *search_mtd;
+ struct mtd_info *chip_0_remainder_mtd = 0;
+ struct mtd_info *medium_remainder_mtd = 0;
+ struct mtd_info *concatenate[2];
+
+ /*
+ * Here we declare the static strings we use to name partitions. We use
+ * static strings because, as of 2.6.31, the partitioning code *always*
+ * registers the partition MTDs it creates and leaves behind *no* other
+ * trace of its work. So, once we've created a partition, we must search
+ * the master MTD table to find the MTDs we created. Since we're using
+ * static strings, we can simply search the master table for an MTD with
+ * a name field pointing to a known address.
+ */
+ static char *chip_0_boot_name = "gpmi-nfc-0-boot";
+ static char *chip_0_remainder_name = "gpmi-nfc-0-remainder";
+ static char *chip_1_boot_name = "gpmi-nfc-1-boot";
+ static char *medium_remainder_name = "gpmi-nfc-remainder";
+ static char *general_use_name = "gpmi-nfc-general-use";
+
+ /* Check if we're protecting the boot areas.*/
+ if (!rom->boot_area_count) {
+ /*
+ * If control arrives here, we're not protecting the boot areas.
+ * In this case, there are not boot area partitons, and the main
+ * MTD is the general use MTD.
+ */
+ mil->general_use_mtd = &mil->mtd;
+
+ return 0;
+ }
+
+ /*
+ * If control arrives here, we're protecting the boot areas. Check if we
+ * have the MTD support we need.
+ */
+ pr_info("Boot area protection is enabled.\n");
+
+ if (rom->boot_area_count > 1) {
+ /*
+ * If the Boot ROM wants more than one boot area, then we'll
+ * need to create partitions *and* concatenate them.
+ */
+ #if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT)
+ mtd_support_is_adequate = true;
+ #else
+ mtd_support_is_adequate = false;
+ #endif
+ } else if (rom->boot_area_count == 1) {
+ /*
+ * If the Boot ROM wants only one boot area, then we only need
+ * to create partitions -- we don't need to concatenate them.
+ */
+ #if defined(CONFIG_MTD_PARTITIONS)
+ mtd_support_is_adequate = true;
+ #else
+ mtd_support_is_adequate = false;
+ #endif
+ } else {
+ /*
+ * If control arrives here, we're protecting the boot area, but
+ * somehow the boot area count was set to zero. This doesn't
+ * make any sense.
+ */
+ dev_err(dev, "Internal error: boot area count is "
+ "incorrectly set to zero.");
+ return -ENXIO;
+ }
+
+ if (!mtd_support_is_adequate) {
+ dev_err(dev, "Configured MTD support is inadequate to "
+ "protect the boot area(s).");
+ return -ENXIO;
+ }
+
+ /*
+ * If control arrives here, we're protecting boot areas and we have
+ * everything we need to do so.
+ *
+ * We have special code to handle the case for one boot area.
+ *
+ * The code that handles "more than one" boot area actually only handles
+ * two. We *could* write the general case, but that would take a lot of
+ * time to both write and test -- and, right now, we don't have a chip
+ * that cares.
+ */
+
+ /* Check if a boot area is larger than a single chip. */
+ if (rom->boot_area_size_in_bytes > info->chip_size_in_bytes) {
+ dev_emerg(dev, "Boot area size is larger than a chip");
+ return -ENXIO;
+ }
+
+ if (rom->boot_area_count == 1) {
+#if defined(CONFIG_MTD_PARTITIONS)
+ /*
+ * We partition the medium like so:
+ *
+ * +------+----------------------------------------------------+
+ * | Boot | General Use |
+ * +------+----------------------------------------------------+
+ */
+
+ /* Chip 0 Boot */
+ partitions[0].name = chip_0_boot_name;
+ partitions[0].offset = 0;
+ partitions[0].size = rom->boot_area_size_in_bytes;
+ partitions[0].mask_flags = 0;
+
+ /* General Use */
+ partitions[1].name = general_use_name;
+ partitions[1].offset = rom->boot_area_size_in_bytes;
+ partitions[1].size = MTDPART_SIZ_FULL;
+ partitions[1].mask_flags = 0;
+
+ /* Construct and register the partitions. */
+ add_mtd_partitions(mtd, partitions, 2);
+
+ /* Find the general use MTD. */
+ i = 0;
+ while ((search_mtd = get_mtd_device(0, i))) {
+ /* Check if we got nonsense. */
+ if ((!search_mtd) || (search_mtd == ERR_PTR(-ENODEV)))
+ break;
+
+ /* Check if the current MTD is one of our remainders. */
+ if (search_mtd->name == general_use_name)
+ mil->general_use_mtd = search_mtd;
+
+ /* Put the MTD back. We only wanted a quick look. */
+ put_mtd_device(search_mtd);
+
+ i++;
+ }
+
+ if (!mil->general_use_mtd) {
+ dev_emerg(dev, "Can't find general use MTD");
+ BUG();
+ }
+#endif
+ } else if (rom->boot_area_count == 2) {
+#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT)
+ /*
+ * If control arrives here, there is more than one boot area.
+ * We partition the medium and concatenate the remainders like
+ * so:
+ *
+ * --- Chip 0 --- --- Chip 1 --- ... ------- Chip N -------
+ * / \ / \
+ * +----+----------+----+--------------- ... ------------------+
+ * |Boot|Remainder |Boot| Remainder |
+ * +----+----------+----+--------------- ... ------------------+
+ * | | / /
+ * | | / /
+ * | | / /
+ * | |/ /
+ * +----------+----------- ... ----------------------+
+ * | General Use |
+ * +---------------------- ... ----------------------+
+ *
+ * Notice that the results we leave in the master MTD table
+ * look like this:
+ *
+ * * Chip 0 Boot Area
+ * * Chip 1 Boot Area
+ * * General Use
+ *
+ * Some user space programs expect the boot partitions to
+ * appear first. This is naive, but let's try not to cause
+ * any trouble, where we can avoid it.
+ */
+
+ /* Chip 0 Boot */
+ partitions[0].name = chip_0_boot_name;
+ partitions[0].offset = 0;
+ partitions[0].size = rom->boot_area_size_in_bytes;
+ partitions[0].mask_flags = 0;
+
+ /* Chip 1 Boot */
+ partitions[1].name = chip_1_boot_name;
+ partitions[1].offset = nand->chipsize;
+ partitions[1].size = rom->boot_area_size_in_bytes;
+ partitions[1].mask_flags = 0;
+
+ /* Chip 0 Remainder */
+ partitions[2].name = chip_0_remainder_name;
+ partitions[2].offset = rom->boot_area_size_in_bytes;
+ partitions[2].size = nand->chipsize -
+ rom->boot_area_size_in_bytes;
+ partitions[2].mask_flags = 0;
+
+ /* Medium Remainder */
+ partitions[3].name = medium_remainder_name;
+ partitions[3].offset = nand->chipsize +
+ rom->boot_area_size_in_bytes;
+ partitions[3].size = MTDPART_SIZ_FULL;
+ partitions[3].mask_flags = 0;
+
+ /* Construct and register the partitions. */
+ add_mtd_partitions(mtd, partitions, 4);
+
+ /* Find the remainder partitions. */
+ i = 0;
+ while ((search_mtd = get_mtd_device(0, i))) {
+ /* Check if we got nonsense. */
+ if ((!search_mtd) || (search_mtd == ERR_PTR(-ENODEV)))
+ break;
+
+ /* Check if the current MTD is one of our remainders. */
+ if (search_mtd->name == chip_0_remainder_name)
+ chip_0_remainder_mtd = search_mtd;
+
+ if (search_mtd->name == medium_remainder_name)
+ medium_remainder_mtd = search_mtd;
+
+ /* Put the MTD back. We only wanted a quick look. */
+ put_mtd_device(search_mtd);
+
+ i++;
+ }
+
+ if (!chip_0_remainder_mtd || !medium_remainder_mtd) {
+ dev_emerg(dev, "Can't find remainder partitions");
+ BUG();
+ }
+
+ /*
+ * Unregister the remainder MTDs. Note that we are *not*
+ * destroying these MTDs -- we're just removing from the
+ * globally-visible list. There's no need for anyone to see
+ * these.
+ */
+ del_mtd_device(chip_0_remainder_mtd);
+ del_mtd_device(medium_remainder_mtd);
+
+ /* Concatenate the remainders and register the result. */
+ concatenate[0] = chip_0_remainder_mtd;
+ concatenate[1] = medium_remainder_mtd;
+
+ mil->general_use_mtd = mtd_concat_create(concatenate,
+ 2, general_use_name);
+
+ add_mtd_device(mil->general_use_mtd);
+#endif
+
+ } else {
+ dev_err(dev, "Boot area count greater than two is "
+ "unimplemented.\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+/**
+ * mil_boot_areas_exit() - Shuts down boot areas.
+ *
+ * @this: Per-device data.
+ */
+static void mil_boot_areas_exit(struct gpmi_nfc_data *this)
+{
+ struct boot_rom_geometry *rom = &this->rom_geometry;
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+
+ /* Check if we're protecting the boot areas.*/
+ if (!rom->boot_area_count) {
+ /*
+ * If control arrives here, we're not protecting the boot areas.
+ * That means we never created any boot area partitions, and the
+ * general use MTD is just the main MTD.
+ */
+ mil->general_use_mtd = 0;
+
+ return;
+ }
+
+ /*
+ * If control arrives here, we're protecting the boot areas.
+ *
+ * Start by checking if there is more than one boot area. If so, then
+ * we both partitioned the medium and then concatenated some of the
+ * partitions to form the general use MTD. The first step is to get rid
+ * of the concatenation.
+ */
+ #if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT)
+ if (rom->boot_area_count > 1) {
+ del_mtd_device(mil->general_use_mtd);
+ mtd_concat_destroy(mil->general_use_mtd);
+ }
+ #endif
+
+ /*
+ * At this point, we're left only with the partitions of the main MTD.
+ * Delete them.
+ */
+ #if defined(CONFIG_MTD_PARTITIONS)
+ del_mtd_partitions(mtd);
+ #endif
+
+ /* The general use MTD no longer exists. */
+ mil->general_use_mtd = 0;
+}
+
+/**
+ * mil_construct_ubi_partitions() - Constructs partitions for UBI.
+ *
+ * MTD uses a 64-bit value to express the size of MTDs, but UBI is still using
+ * a 32-bit value. For this reason, UBI can't work on top of an MTD with size
+ * greater than 2GiB. In this function, we examine the general use MTD and, if
+ * it's larger than 2GiB, we construct a set of partitions for that MTD such
+ * that none are too large for UBI to comprehend.
+ *
+ * @this: Per-device data.
+ */
+static void mil_construct_ubi_partitions(struct gpmi_nfc_data *this)
+{
+#if defined(CONFIG_MTD_PARTITIONS)
+ struct device *dev = this->dev;
+ struct mil *mil = &this->mil;
+ unsigned int partition_count;
+ struct mtd_partition *partitions;
+ unsigned int name_size;
+ char *names;
+ unsigned int memory_block_size;
+ unsigned int i;
+
+ static const char *name_prefix = "gpmi-nfc-ubi-";
+
+ /*
+ * If the general use MTD isn't larger than 2GiB, we have nothing to do.
+ */
+ if (mil->general_use_mtd->size <= SZ_2G)
+ return;
+
+ /*
+ * If control arrives here, the general use MTD is larger than 2GiB. We
+ * need to split it up into some number of partitions. Find out how many
+ * 2GiB partitions we'll be creating.
+ */
+ partition_count = mil->general_use_mtd->size >> 31;
+
+ /*
+ * If the MTD size doesn't evenly divide by 2GiB, we'll need another
+ * partition to hold the extra.
+ */
+ if (mil->general_use_mtd->size & ((1 << 30) - 1))
+ partition_count++;
+
+ /*
+ * We're going to allocate a single memory block to contain all the
+ * partition structures and their names. Calculate how large it must be.
+ */
+ name_size = strlen(name_prefix) + 4;
+
+ memory_block_size = (sizeof(*partitions) + name_size) * partition_count;
+
+ /*
+ * Attempt to allocate the block.
+ */
+ partitions = kzalloc(memory_block_size, GFP_KERNEL);
+ if (!partitions) {
+ dev_err(dev, "Could not allocate memory for UBI partitions.\n");
+ return;
+ }
+
+ names = (char *)(partitions + partition_count);
+
+ /* Loop over partitions, filling in the details. */
+ for (i = 0; i < partition_count; i++) {
+ partitions[i].name = names;
+ partitions[i].size = SZ_2G;
+ partitions[i].offset = MTDPART_OFS_NXTBLK;
+
+ sprintf(names, "%s%u", name_prefix, i);
+ names += name_size;
+ }
+
+ /* Adjust the last partition to take up the remainder. */
+ partitions[i - 1].size = MTDPART_SIZ_FULL;
+
+ /* Record everything in the device data structure. */
+ mil->partitions = partitions;
+ mil->partition_count = partition_count;
+ mil->ubi_partition_memory = partitions;
+#endif
+}
+
+/**
+ * mil_partitions_init() - Initializes partitions.
+ *
+ * @this: Per-device data.
+ */
+static int mil_partitions_init(struct gpmi_nfc_data *this)
+{
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+ int error;
+
+ /*
+ * Set up the boot areas. When this function returns, if there has been
+ * no error, the boot area partitions (if any) will have been created
+ * and registered. Also, the general_use_mtd field will point to an MTD
+ * we can use.
+ */
+ error = mil_boot_areas_init(this);
+ if (error)
+ return error;
+
+ /*
+ * If we've been told to, register the MTD that represents the entire
+ * medium. Normally, we don't register the main MTD because we only want
+ * to expose the medium through the boot area partitions and the general
+ * use partition.
+ *
+ * We do this *after* setting up the boot areas because, for historical
+ * reasons, we like the lowest-numbered MTDs to be the boot areas.
+ */
+ if (register_main_mtd) {
+ pr_info("Registering the main MTD.\n");
+ add_mtd_device(mtd);
+ }
+
+#if defined(CONFIG_MTD_PARTITIONS)
+ /*
+ * If control arrives here, partitioning is available.
+ *
+ * There are three possible sets of partitions we might apply, in order
+ * of decreasing priority:
+ *
+ * 1) Partitions dynamically discovered from sources defined by the
+ * platform. These can come from, for example, the command line or
+ * a partition table.
+ *
+ * 2) Partitions attached to the platform data.
+ *
+ * 3) Partitions we generate to deal with limitations in UBI.
+ *
+ * Recall that the pointer to the general use MTD *may* just point to
+ * the main MTD.
+ */
+
+ /*
+ * First, try to get partition information from the sources defined by
+ * the platform.
+ */
+ if (pdata->partition_source_types)
+ mil->partition_count =
+ parse_mtd_partitions(mil->general_use_mtd,
+ pdata->partition_source_types,
+ &mil->partitions, 0);
+
+ /*
+ * Check if we got anything. If not, then accept whatever partitions are
+ * attached to the platform data.
+ */
+ if ((mil->partition_count <= 0) && (pdata->partitions)) {
+ mil->partition_count = mil->partition_count;
+ mil->partitions = mil->partitions;
+ }
+
+ /*
+ * If we still don't have any partitions to apply, then we might want to
+ * apply some of our own, to account for UBI's limitations.
+ */
+ if (!mil->partition_count)
+ mil_construct_ubi_partitions(this);
+
+ /* If we came up with any partitions, apply them. */
+ if (mil->partition_count)
+ add_mtd_partitions(mil->general_use_mtd,
+ mil->partitions,
+ mil->partition_count);
+#endif
+ return 0;
+}
+
+/**
+ * mil_partitions_exit() - Shuts down partitions.
+ *
+ * @this: Per-device data.
+ */
+static void mil_partitions_exit(struct gpmi_nfc_data *this)
+{
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+
+ /* Check if we applied any partitions to the general use MTD. */
+ #if defined(CONFIG_MTD_PARTITIONS)
+
+ if (mil->partition_count)
+ del_mtd_partitions(mil->general_use_mtd);
+
+ kfree(mil->ubi_partition_memory);
+
+ #endif
+
+ /*
+ * If we were told to register the MTD that represents the entire
+ * medium, unregister it now. Note that this does *not* "destroy" the
+ * MTD - it merely unregisters it. That's important because all our
+ * other MTDs depend on this one.
+ */
+ if (register_main_mtd)
+ del_mtd_device(mtd);
+
+ /* Tear down the boot areas. */
+ mil_boot_areas_exit(this);
+}
+
+/*
+ * This function is used to set the mtd->pagesize, mtd->oobsize,
+ * mtd->erasesize. Yes, we also do some initialization.
+ *
+ * Return with the bus width. 0 for 8-bit, -1 for error.
+ */
+static int gpmi_init_size(struct mtd_info *mtd, struct nand_chip *nand,
+ u8 *id_bytes)
+{
+ struct gpmi_nfc_data *this = nand->priv;
+ struct nfc_hal *nfc = this->nfc;
+ struct mil *mil = &this->mil;
+ struct nand_ecclayout *layout = &mil->oob_layout;
+ struct nand_device_info *info;
+ struct gpmi_nfc_timing timing;
+ int error;
+
+ /* Look up this device in our database. */
+ info = nand_device_get_info(id_bytes);
+ if (!info) {
+ pr_err("Unrecognized NAND Flash device.\n");
+ return -1;
+ }
+
+ /* Display the information we discovered. */
+ #if defined(DETAILED_INFO)
+ pr_info("-----------------------------\n");
+ pr_info("NAND Flash Device Information\n");
+ pr_info("-----------------------------\n");
+ nand_device_print_info(info);
+ #endif
+
+ /*
+ * Init the right NAND/MTD parameters which will be used
+ * in the following mil_set_geometry().
+ */
+ mtd->writesize = 1 << (fls(info->page_total_size_in_bytes) - 1);
+ mtd->erasesize = mtd->writesize * info->block_size_in_pages;
+ mtd->oobsize = info->page_total_size_in_bytes - mtd->writesize;
+ nand->chipsize = info->chip_size_in_bytes;
+
+ /* Configure the struct nand_ecclayout. */
+ layout->eccbytes = 0;
+ layout->oobavail = mtd->oobsize;
+ layout->oobfree[0].offset = 0;
+ layout->oobfree[0].length = mtd->oobsize;
+
+ nand->ecc.layout = layout;
+
+ /*
+ * Copy the device info into the per-device data. We can't just keep
+ * the pointer because that storage is reclaimed after initialization.
+ */
+ this->device_info = *info;
+ this->device_info.description = kstrdup(info->description, GFP_KERNEL);
+
+ /* Set up geometry. */
+ error = mil_set_geometry(this);
+ if (error)
+ return -1;
+
+ /* Set up timing. */
+ timing.data_setup_in_ns = info->data_setup_in_ns;
+ timing.data_hold_in_ns = info->data_hold_in_ns;
+ timing.address_setup_in_ns = info->address_setup_in_ns;
+ timing.gpmi_sample_delay_in_ns = info->gpmi_sample_delay_in_ns;
+ timing.tREA_in_ns = info->tREA_in_ns;
+ timing.tRLOH_in_ns = info->tRLOH_in_ns;
+ timing.tRHOH_in_ns = info->tRHOH_in_ns;
+
+ error = nfc->set_timing(this, &timing);
+ if (error)
+ return -1;
+
+ if (nfc->extra_init) {
+ error = nfc->extra_init(this);
+ if (error != 0)
+ return -1;
+ }
+
+ /* We only use 8-bit bus now, not 16-bit. */
+ return 0;
+}
+
+/**
+ * gpmi_nfc_mil_init() - Initializes the MTD Interface Layer.
+ *
+ * @this: Per-device data.
+ */
+int gpmi_nfc_mil_init(struct gpmi_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+ struct nand_chip *nand = &mil->nand;
+ int error = 0;
+
+ /* Initialize MIL data. */
+ mil->current_chip = -1;
+ mil->command_length = 0;
+
+ mil->page_buffer_virt = 0;
+ mil->page_buffer_phys = ~0;
+ mil->page_buffer_size = 0;
+
+ /* Initialize the MTD data structures. */
+ mtd->priv = nand;
+ mtd->name = "gpmi-nfc-main";
+ mtd->owner = THIS_MODULE;
+ nand->priv = this;
+
+ /*
+ * Signal Control
+ */
+ nand->cmd_ctrl = mil_cmd_ctrl;
+
+ /*
+ * Chip Control
+ *
+ * We rely on the reference implementations of:
+ * - cmdfunc
+ * - waitfunc
+ */
+ nand->dev_ready = mil_dev_ready;
+ nand->select_chip = mil_select_chip;
+
+ /*
+ * Low-level I/O
+ *
+ * We don't support a 16-bit NAND Flash bus, so we don't implement
+ * read_word.
+ *
+ * We rely on the reference implentation of verify_buf.
+ */
+ nand->read_byte = mil_read_byte;
+ nand->read_buf = mil_read_buf;
+ nand->write_buf = mil_write_buf;
+
+ /*
+ * ECC Control
+ *
+ * None of these functions are necessary for us:
+ * - ecc.hwctl
+ * - ecc.calculate
+ * - ecc.correct
+ */
+
+ /*
+ * ECC-aware I/O
+ *
+ * We rely on the reference implementations of:
+ * - ecc.read_page_raw
+ * - ecc.write_page_raw
+ */
+ nand->ecc.read_page = mil_ecc_read_page;
+ nand->ecc.write_page = mil_ecc_write_page;
+
+ /*
+ * High-level I/O
+ *
+ * We rely on the reference implementations of:
+ * - write_page
+ * - erase_cmd
+ */
+ nand->ecc.read_oob = mil_ecc_read_oob;
+ nand->ecc.write_oob = mil_ecc_write_oob;
+
+ /*
+ * Bad Block Management
+ *
+ * We rely on the reference implementations of:
+ * - block_bad
+ * - block_markbad
+ */
+ nand->block_bad = mil_block_bad;
+ nand->scan_bbt = mil_scan_bbt;
+ nand->init_size = gpmi_init_size;
+ nand->badblock_pattern = &gpmi_bbt_descr;
+
+ /*
+ * 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.
+ */
+
+ /*
+ * Set up NAND Flash options. Specifically:
+ *
+ * - Disallow partial page writes.
+ */
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ /*
+ * Tell the NAND Flash MTD system that we'll be handling ECC with our
+ * own hardware. It turns out that we still have to fill in the ECC size
+ * because the MTD code will divide by it -- even though it doesn't
+ * actually care.
+ */
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.size = 1;
+
+ /* Allocate a temporary DMA buffer for reading ID in the nand_scan() */
+ this->nfc_geometry.payload_size_in_bytes = 1024;
+ this->nfc_geometry.auxiliary_size_in_bytes = 128;
+ error = mil_alloc_dma_buffer(this);
+ if (error)
+ goto exit_dma_allocation;
+
+ /*
+ * 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.
+ */
+ pr_info("Scanning for NAND Flash chips...\n");
+ error = nand_scan(mtd, pdata->max_chip_count);
+ if (error) {
+ dev_err(dev, "Chip scan failed\n");
+ goto exit_nand_scan;
+ }
+
+ /*
+ * Hook some operations at the MTD level. See the descriptions of the
+ * saved function pointer fields for details about why we hook these.
+ */
+ mil->hooked_block_markbad = mtd->block_markbad;
+ mtd->block_markbad = mil_hook_block_markbad;
+
+ /* Construct partitions as necessary. */
+ error = mil_partitions_init(this);
+ if (error)
+ goto exit_partitions;
+
+ return 0;
+
+exit_partitions:
+ nand_release(&mil->mtd);
+exit_nand_scan:
+ mil_free_dma_buffer(this);
+exit_dma_allocation:
+ return error;
+}
+
+/**
+ * gpmi_nfc_mil_exit() - Shuts down the MTD Interface Layer.
+ *
+ * @this: Per-device data.
+ */
+void gpmi_nfc_mil_exit(struct gpmi_nfc_data *this)
+{
+ struct mil *mil = &this->mil;
+
+ /* Shut down partitions as necessary. */
+ mil_partitions_exit(this);
+
+ /* Get MTD to let go of our MTD. */
+ nand_release(&mil->mtd);
+
+ /* Free all the DMA buffer, if it's been allocated. */
+ mil_free_dma_buffer(this);
+}
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-common.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-common.c
new file mode 100644
index 000000000000..0cd0b39141fd
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-common.c
@@ -0,0 +1,59 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "gpmi-nfc.h"
+
+/**
+ * gpmi_nfc_rom_helper_set_geometry() - Sets geometry for the Boot ROM Helper.
+ *
+ * @this: Per-device data.
+ */
+int gpmi_nfc_rom_helper_set_geometry(struct gpmi_nfc_data *this)
+{
+ struct boot_rom_geometry *geometry = &this->rom_geometry;
+
+ /*
+ * Set the boot block stride size.
+ *
+ * In principle, we should be reading this from the OTP bits, since
+ * that's where the ROM is going to get it. In fact, we don't have any
+ * way to read the OTP bits, so we go with the default and hope for the
+ * best.
+ */
+
+ geometry->stride_size_in_pages = 64;
+
+ /*
+ * Set the search area stride exponent.
+ *
+ * In principle, we should be reading this from the OTP bits, since
+ * that's where the ROM is going to get it. In fact, we don't have any
+ * way to read the OTP bits, so we go with the default and hope for the
+ * best.
+ */
+
+ geometry->search_area_stride_exponent = 2;
+
+ /* Return success. */
+
+ return 0;
+
+}
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c
new file mode 100644
index 000000000000..78f6a438125b
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v0.c
@@ -0,0 +1,330 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "gpmi-nfc.h"
+
+/*
+ * Useful variables for Boot ROM Helper version 0.
+ */
+
+static const char *fingerprint = "STMP";
+
+/**
+ * set_geometry() - Sets geometry for the Boot ROM Helper.
+ *
+ * @this: Per-device data.
+ */
+static int set_geometry(struct gpmi_nfc_data *this)
+{
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct boot_rom_geometry *geometry = &this->rom_geometry;
+ struct nand_chip *nand = &this->mil.nand;
+ int error;
+
+ /* Version-independent geometry. */
+ error = gpmi_nfc_rom_helper_set_geometry(this);
+ if (error)
+ return error;
+
+ /*
+ * Check if the platform data indicates we are to protect the boot area.
+ */
+ if (!pdata->boot_area_size_in_bytes) {
+ geometry->boot_area_count = 0;
+ geometry->boot_area_size_in_bytes = 0;
+ return 0;
+ }
+
+ /*
+ * If control arrives here, we are supposed to set up partitions to
+ * protect the boot areas. In this version of the ROM, the number of
+ * boot areas and their size depends on the number of chips.
+ */
+ if (nand->numchips == 1) {
+ geometry->boot_area_count = 1;
+ geometry->boot_area_size_in_bytes =
+ pdata->boot_area_size_in_bytes * 2;
+ } else {
+ geometry->boot_area_count = 2;
+ geometry->boot_area_size_in_bytes =
+ pdata->boot_area_size_in_bytes;
+ }
+ return 0;
+}
+
+/**
+ * check_transcription_stamp() - Checks for a transcription stamp.
+ *
+ * Returns 0 if a stamp is not found.
+ *
+ * @this: Per-device data.
+ */
+static int check_transcription_stamp(struct gpmi_nfc_data *this)
+{
+ struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+ struct nand_chip *nand = &mil->nand;
+ unsigned int search_area_size_in_strides;
+ unsigned int stride;
+ unsigned int page;
+ loff_t byte;
+ uint8_t *buffer = nand->buffers->databuf;
+ int saved_chip_number;
+ int found_an_ncb_fingerprint = false;
+
+ /* Compute the number of strides in a search area. */
+ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
+
+ /* Select chip 0. */
+ saved_chip_number = mil->current_chip;
+ nand->select_chip(mtd, 0);
+
+ /*
+ * Loop through the first search area, looking for the NCB fingerprint.
+ */
+ pr_info("Scanning for an NCB fingerprint...\n");
+
+ for (stride = 0; stride < search_area_size_in_strides; stride++) {
+ /* Compute the page and byte addresses. */
+ page = stride * rom_geo->stride_size_in_pages;
+ byte = page * mtd->writesize;
+
+ pr_info(" Looking for a fingerprint in page 0x%x\n", page);
+
+ /*
+ * Read the NCB fingerprint. The fingerprint is four bytes long
+ * and starts in the 12th byte of the page.
+ */
+ nand->cmdfunc(mtd, NAND_CMD_READ0, 12, page);
+ nand->read_buf(mtd, buffer, strlen(fingerprint));
+
+ /* Look for the fingerprint. */
+ if (!memcmp(buffer, fingerprint, strlen(fingerprint))) {
+ found_an_ncb_fingerprint = true;
+ break;
+ }
+
+ }
+
+ /* Deselect chip 0. */
+ nand->select_chip(mtd, saved_chip_number);
+
+ if (found_an_ncb_fingerprint)
+ pr_info(" Found a fingerprint\n");
+ else
+ pr_info(" No fingerprint found\n");
+ return found_an_ncb_fingerprint;
+}
+
+/**
+ * write_transcription_stamp() - Writes a transcription stamp.
+ *
+ * @this: Per-device data.
+ */
+static int write_transcription_stamp(struct gpmi_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+ struct nand_device_info *info = &this->device_info;
+ struct mil *mil = &this->mil;
+ struct mtd_info *mtd = &mil->mtd;
+ struct nand_chip *nand = &mil->nand;
+ unsigned int block_size_in_pages;
+ unsigned int search_area_size_in_strides;
+ unsigned int search_area_size_in_pages;
+ unsigned int search_area_size_in_blocks;
+ unsigned int block;
+ unsigned int stride;
+ unsigned int page;
+ loff_t byte;
+ uint8_t *buffer = nand->buffers->databuf;
+ int saved_chip_number;
+ int status;
+
+ /* Compute the search area geometry. */
+ block_size_in_pages = info->block_size_in_pages;
+ search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
+ search_area_size_in_pages = search_area_size_in_strides *
+ rom_geo->stride_size_in_pages;
+ search_area_size_in_blocks =
+ (search_area_size_in_pages + (block_size_in_pages - 1)) /
+ block_size_in_pages;
+
+ #if defined(DETAILED_INFO)
+ pr_info("--------------------\n");
+ pr_info("Search Area Geometry\n");
+ pr_info("--------------------\n");
+ pr_info("Search Area Size in Blocks : %u", search_area_size_in_blocks);
+ pr_info("Search Area Size in Strides: %u", search_area_size_in_strides);
+ pr_info("Search Area Size in Pages : %u", search_area_size_in_pages);
+ #endif
+
+ /* Select chip 0. */
+ saved_chip_number = mil->current_chip;
+ nand->select_chip(mtd, 0);
+
+ /* Loop over blocks in the first search area, erasing them. */
+ pr_info("Erasing the search area...\n");
+
+ for (block = 0; block < search_area_size_in_blocks; block++) {
+ /* Compute the page address. */
+ page = block * block_size_in_pages;
+
+ /* Erase this block. */
+ pr_info(" Erasing block 0x%x\n", block);
+ nand->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ nand->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+
+ /* Wait for the erase to finish. */
+ status = nand->waitfunc(mtd, nand);
+ if (status & NAND_STATUS_FAIL)
+ dev_err(dev, "[%s] Erase failed.\n", __func__);
+ }
+
+ /* Write the NCB fingerprint into the page buffer. */
+ memset(buffer, ~0, mtd->writesize);
+ memset(nand->oob_poi, ~0, mtd->oobsize);
+ memcpy(buffer + 12, fingerprint, strlen(fingerprint));
+
+ /* Loop through the first search area, writing NCB fingerprints. */
+ pr_info("Writing NCB fingerprints...\n");
+ for (stride = 0; stride < search_area_size_in_strides; stride++) {
+ /* Compute the page and byte addresses. */
+ page = stride * rom_geo->stride_size_in_pages;
+ byte = page * mtd->writesize;
+
+ /* Write the first page of the current stride. */
+ pr_info(" Writing an NCB fingerprint in page 0x%x\n", page);
+ nand->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+ nand->ecc.write_page_raw(mtd, nand, buffer);
+ nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ /* Wait for the write to finish. */
+ status = nand->waitfunc(mtd, nand);
+ if (status & NAND_STATUS_FAIL)
+ dev_err(dev, "[%s] Write failed.\n", __func__);
+ }
+
+ /* Deselect chip 0. */
+ nand->select_chip(mtd, saved_chip_number);
+ return 0;
+}
+
+static int imx23_rom_extra_init(struct gpmi_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct mil *mil = &this->mil;
+ struct nand_chip *nand = &mil->nand;
+ struct mtd_info *mtd = &mil->mtd;
+ struct nand_device_info *info = &this->device_info;
+ unsigned int block_count;
+ unsigned int block;
+ int chip;
+ int page;
+ loff_t byte;
+ uint8_t block_mark;
+ int error = 0;
+
+ /*
+ * If control arrives here, we can't use block mark swapping, which
+ * means we're forced to use transcription. First, scan for the
+ * transcription stamp. If we find it, then we don't have to do
+ * anything -- the block marks are already transcribed.
+ */
+ if (check_transcription_stamp(this))
+ return 0;
+
+ /*
+ * If control arrives here, we couldn't find a transcription stamp, so
+ * so we presume the block marks are in the conventional location.
+ */
+ pr_info("Transcribing bad block marks...\n");
+
+ /* Compute the number of blocks in the entire medium. */
+ block_count = info->chip_size_in_bytes >> nand->phys_erase_shift;
+
+ /*
+ * Loop over all the blocks in the medium, transcribing block marks as
+ * we go.
+ */
+ for (block = 0; block < block_count; block++) {
+ /*
+ * Compute the chip, page and byte addresses for this block's
+ * conventional mark.
+ */
+ chip = block >> (nand->chip_shift - nand->phys_erase_shift);
+ page = block << (nand->phys_erase_shift - nand->page_shift);
+ byte = block << nand->phys_erase_shift;
+
+ /* Select the chip. */
+ nand->select_chip(mtd, chip);
+
+ /* Send the command to read the conventional block mark. */
+ nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+
+ /* Read the conventional block mark. */
+ block_mark = nand->read_byte(mtd);
+
+ /*
+ * Check if the block is marked bad. If so, we need to mark it
+ * again, but this time the result will be a mark in the
+ * location where we transcribe block marks.
+ *
+ * Notice that we have to explicitly set the marking_a_bad_block
+ * member before we call through the block_markbad function
+ * pointer in the owning struct nand_chip. If we could call
+ * though the block_markbad function pointer in the owning
+ * struct mtd_info, which we have hooked, then this would be
+ * taken care of for us. Unfortunately, we can't because that
+ * higher-level code path will do things like consulting the
+ * in-memory bad block table -- which doesn't even exist yet!
+ * So, we have to call at a lower level and handle some details
+ * ourselves.
+ */
+ if (block_mark != 0xff) {
+ pr_info("Transcribing mark in block %u\n", block);
+ mil->marking_a_bad_block = true;
+ error = nand->block_markbad(mtd, byte);
+ mil->marking_a_bad_block = false;
+ if (error)
+ dev_err(dev, "Failed to mark block bad with "
+ "error %d\n", error);
+ }
+
+ /* Deselect the chip. */
+ nand->select_chip(mtd, -1);
+ }
+
+ /* Write the stamp that indicates we've transcribed the block marks. */
+ write_transcription_stamp(this);
+ return 0;
+}
+
+/* This structure represents the Boot ROM Helper for this version. */
+struct boot_rom_helper gpmi_nfc_boot_rom_helper_v0 = {
+ .version = 0,
+ .description = "Single/dual-chip boot area, "
+ "no block mark swapping",
+ .swap_block_mark = false,
+ .set_geometry = set_geometry,
+ .rom_extra_init = imx23_rom_extra_init,
+};
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v1.c b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v1.c
new file mode 100644
index 000000000000..699dea8d9533
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc-rom-v1.c
@@ -0,0 +1,80 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "gpmi-nfc.h"
+
+/**
+ * set_geometry() - Sets geometry for the Boot ROM Helper.
+ *
+ * @this: Per-device data.
+ */
+static int set_geometry(struct gpmi_nfc_data *this)
+{
+ struct gpmi_nfc_platform_data *pdata = this->pdata;
+ struct boot_rom_geometry *geometry = &this->rom_geometry;
+ int error;
+
+ /* Version-independent geometry. */
+
+ error = gpmi_nfc_rom_helper_set_geometry(this);
+
+ if (error)
+ return error;
+
+ /*
+ * Check if the platform data indicates we are to protect the boot area.
+ */
+
+ if (!pdata->boot_area_size_in_bytes) {
+ geometry->boot_area_count = 0;
+ geometry->boot_area_size_in_bytes = 0;
+ return 0;
+ }
+
+ /*
+ * If control arrives here, we are supposed to set up partitions to
+ * protect the boot areas. In this version of the ROM, we support only
+ * one boot area.
+ */
+
+ geometry->boot_area_count = 1;
+
+ /*
+ * Use the platform's boot area size.
+ */
+
+ geometry->boot_area_size_in_bytes = pdata->boot_area_size_in_bytes;
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/* This structure represents the Boot ROM Helper for this version. */
+
+struct boot_rom_helper gpmi_nfc_boot_rom_helper_v1 = {
+ .version = 1,
+ .description = "Single-chip boot area, "
+ "block mark swapping supported",
+ .swap_block_mark = true,
+ .set_geometry = set_geometry,
+};
diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
new file mode 100644
index 000000000000..3ba69d876b53
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nfc/gpmi-nfc.h
@@ -0,0 +1,588 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __DRIVERS_MTD_NAND_GPMI_NFC_H
+#define __DRIVERS_MTD_NAND_GPMI_NFC_H
+
+/* Linux header files. */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/concat.h>
+#include <linux/gpmi-nfc.h>
+#include <asm/sizes.h>
+
+/* Platform header files. */
+
+#include <mach/system.h>
+#include <mach/dmaengine.h>
+#include <mach/clock.h>
+
+/* Driver header files. */
+
+#include "../nand_device_info.h"
+
+/*
+ *------------------------------------------------------------------------------
+ * Fundamental Macros
+ *------------------------------------------------------------------------------
+ */
+
+/* Define this macro to enable detailed information messages. */
+
+#define DETAILED_INFO
+
+/*
+ *------------------------------------------------------------------------------
+ * Fundamental Data Structures
+ *------------------------------------------------------------------------------
+ */
+
+/**
+ * struct resources - The collection of resources the driver needs.
+ *
+ * @gpmi_regs: A pointer to the GPMI registers.
+ * @bch_regs: A pointer to the BCH registers.
+ * @bch_interrupt: The BCH interrupt number.
+ * @dma_low_channel: The low DMA channel.
+ * @dma_high_channel: The high DMA channel.
+ * @dma_interrupt: The DMA interrupt number.
+ * @clock: A pointer to the struct clk for the NFC's clock.
+ */
+
+struct resources {
+ void *gpmi_regs;
+ void *bch_regs;
+ unsigned int bch_low_interrupt;
+ unsigned int bch_high_interrupt;
+ unsigned int dma_low_channel;
+ unsigned int dma_high_channel;
+ unsigned int dma_low_interrupt;
+ unsigned int dma_high_interrupt;
+ struct clk *clock;
+};
+
+/**
+ * struct mil - State for the MTD Interface Layer.
+ *
+ * @nand: The NAND Flash MTD data structure that represents
+ * the NAND Flash medium.
+ * @mtd: The MTD data structure that represents the NAND
+ * Flash medium.
+ * @oob_layout: A structure that describes how bytes are laid out
+ * in the OOB.
+ * @general_use_mtd: A pointer to an MTD we export for general use.
+ * This *may* simply be a pointer to the mtd field, if
+ * we've been instructed NOT to protect the boot
+ * areas.
+ * @partitions: A pointer to a set of partitions applied to the
+ * general use MTD.
+ * @partition_count: The number of partitions.
+ * @ubi_partition_memory: If not NULL, a block of memory used to create a set
+ * of partitions that help with the problem that UBI
+ * can't handle an MTD larger than 2GiB.
+ * @current_chip: The chip currently selected by the NAND Fash MTD
+ * code. A negative value indicates that no chip is
+ * selected.
+ * @command_length: The length of the command that appears in the
+ * command buffer (see cmd_virt, below).
+ * @inject_ecc_error: Indicates the driver should inject a "fake" ECC
+ * error into 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 by the maximum possible number of
+ * errors that could appear in a single read.
+ * @ignore_bad_block_marks: Indicates we are ignoring bad block marks.
+ * @saved_bbt: A saved pointer to the in-memory NAND Flash MTD bad
+ * block table. See show_device_ignorebad() for more
+ * details.
+ * @marking_a_bad_block: Indicates the caller is marking a bad block. See
+ * mil_ecc_write_oob() for details.
+ * @hooked_block_markbad: A pointer to the block_markbad() function we
+ * we "hooked." See mil_ecc_write_oob() for details.
+ * @cmd_virt: A pointer to a DMA-coherent buffer in which we
+ * accumulate command bytes before we give them to the
+ * NFC layer. See mil_cmd_ctrl() for more details.
+ * @cmd_phys: The physical address for the cmd_virt buffer.
+ * @page_buffer_virt: A pointer to a DMA-coherent buffer we use for
+ * reading and writing pages. This buffer includes
+ * space for both the payload data and the auxiliary
+ * data (including status bytes, but not syndrome
+ * bytes).
+ * @page_buffer_phys: The physical address for the page_buffer_virt
+ * buffer.
+ * @page_buffer_size: The size of the page buffer.
+ * @payload_virt: A pointer to a location in the page buffer used
+ * for payload bytes. The size of this buffer is
+ * determined by struct nfc_geometry.
+ * @payload_phys: The physical address for payload_virt.
+ * @payload_size: The size of the payload area in the page buffer.
+ * @auxiliary_virt: A pointer to a location in the page buffer used
+ * for auxiliary bytes. The size of this buffer is
+ * determined by struct nfc_geometry.
+ * @auxiliary_phys: The physical address for auxiliary_virt.
+ * @auxiliary_size: The size of the auxiliary area in the page buffer.
+ */
+
+#define MIL_COMMAND_BUFFER_SIZE (10)
+
+struct mil {
+
+ /* MTD Data Structures */
+
+ struct nand_chip nand;
+ struct mtd_info mtd;
+ struct nand_ecclayout oob_layout;
+
+ /* Partitioning and Boot Area Protection */
+
+ struct mtd_info *general_use_mtd;
+ struct mtd_partition *partitions;
+ unsigned int partition_count;
+ void *ubi_partition_memory;
+
+ /* General-use Variables */
+
+ int current_chip;
+ unsigned int command_length;
+ int inject_ecc_error;
+ int ignore_bad_block_marks;
+ void *saved_bbt;
+
+ /* MTD Function Pointer Hooks */
+ int marking_a_bad_block;
+ int (*hooked_block_markbad)(struct mtd_info *mtd,
+ loff_t ofs);
+
+ /* DMA Buffers */
+
+ char *cmd_virt;
+ dma_addr_t cmd_phys;
+
+ void *page_buffer_virt;
+ dma_addr_t page_buffer_phys;
+ unsigned int page_buffer_size;
+
+ void *payload_virt;
+ dma_addr_t payload_phys;
+
+ void *auxiliary_virt;
+ dma_addr_t auxiliary_phys;
+
+};
+
+/**
+ * struct nfc_geometry - NFC geometry description.
+ *
+ * This structure describes the NFC's view of the medium geometry.
+ *
+ * @ecc_algorithm: The human-readable name of the ECC algorithm
+ * (e.g., "Reed-Solomon" or "BCH").
+ * @ecc_strength: A number that describes the strength of the ECC
+ * algorithm.
+ * @page_size_in_bytes: The size, in bytes, of a physical page, including
+ * both data and OOB.
+ * @metadata_size_in_bytes: The size, in bytes, of the metadata.
+ * @ecc_chunk_size_in_bytes: The size, in bytes, of a single ECC chunk. Note
+ * the first chunk in the page includes both data and
+ * metadata, so it's a bit larger than this value.
+ * @ecc_chunk_count: The number of ECC chunks in the page,
+ * @payload_size_in_bytes: The size, in bytes, of the payload buffer.
+ * @auxiliary_size_in_bytes: The size, in bytes, of the auxiliary buffer.
+ * @auxiliary_status_offset: The offset into the auxiliary buffer at which
+ * the ECC status appears.
+ * @block_mark_byte_offset: The byte offset in the ECC-based page view at
+ * which the underlying physical block mark appears.
+ * @block_mark_bit_offset: The bit offset into the ECC-based page view at
+ * which the underlying physical block mark appears.
+ */
+
+struct nfc_geometry {
+ char *ecc_algorithm;
+ unsigned int ecc_strength;
+ unsigned int page_size_in_bytes;
+ unsigned int metadata_size_in_bytes;
+ unsigned int ecc_chunk_size_in_bytes;
+ unsigned int ecc_chunk_count;
+ unsigned int payload_size_in_bytes;
+ unsigned int auxiliary_size_in_bytes;
+ unsigned int auxiliary_status_offset;
+ unsigned int block_mark_byte_offset;
+ unsigned int block_mark_bit_offset;
+};
+
+/**
+ * struct boot_rom_geometry - Boot ROM geometry description.
+ *
+ * This structure encapsulates decisions made by the Boot ROM Helper.
+ *
+ * @boot_area_count: The number of boot areas. The first boot area
+ * appears at the beginning of chip 0, the next
+ * at the beginning of chip 1, etc.
+ * @boot_area_size_in_bytes: The size, in bytes, of each boot area.
+ * @stride_size_in_pages: The size of a boot block stride, in pages.
+ * @search_area_stride_exponent: The logarithm to base 2 of the size of a
+ * search area in boot block strides.
+ */
+
+struct boot_rom_geometry {
+ unsigned int boot_area_count;
+ unsigned int boot_area_size_in_bytes;
+ unsigned int stride_size_in_pages;
+ unsigned int search_area_stride_exponent;
+};
+
+/**
+ * struct gpmi_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.
+ * @resources: Information about system resources used by this driver.
+ * @device_info: A structure that contains detailed information about
+ * the NAND Flash device.
+ * @nfc: A pointer to a structure that represents the underlying
+ * NFC hardware.
+ * @nfc_geometry: A description of the medium geometry as viewed by the
+ * NFC.
+ * @rom: A pointer to a structure that represents the underlying
+ * Boot ROM.
+ * @rom_geometry: A description of the medium geometry as viewed by the
+ * Boot ROM.
+ * @mil: A collection of information used by the MTD Interface
+ * Layer.
+ */
+
+struct gpmi_nfc_data {
+
+ /* System Interface */
+ struct device *dev;
+ struct platform_device *pdev;
+ struct gpmi_nfc_platform_data *pdata;
+
+ /* Resources */
+ struct resources resources;
+
+ /* Flash Hardware */
+ struct nand_device_info device_info;
+
+ /* NFC HAL */
+ struct nfc_hal *nfc;
+ struct nfc_geometry nfc_geometry;
+
+ /* Boot ROM Helper */
+ struct boot_rom_helper *rom;
+ struct boot_rom_geometry rom_geometry;
+
+ /* MTD Interface Layer */
+ struct mil mil;
+
+};
+
+/**
+ * struct gpmi_nfc_timing - GPMI NFC timing parameters.
+ *
+ * This structure contains the fundamental timing attributes for the NAND Flash
+ * bus and the GPMI NFC hardware.
+ *
+ * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the
+ * maximum of tDS and tWP. A negative value
+ * indicates this characteristic isn't known.
+ * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the
+ * maximum of tDH, tWH and tREH. A negative value
+ * indicates this characteristic isn't known.
+ * @address_setup_in_ns: The address setup time, in nanoseconds. Usually
+ * the maximum of tCLS, tCS and tALS. A negative
+ * value indicates this characteristic isn't known.
+ * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative value
+ * indicates this characteristic isn't known.
+ * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
+ * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
+ * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic isn't
+ * known.
+ */
+
+struct gpmi_nfc_timing {
+ int8_t data_setup_in_ns;
+ int8_t data_hold_in_ns;
+ int8_t address_setup_in_ns;
+ int8_t gpmi_sample_delay_in_ns;
+ int8_t tREA_in_ns;
+ int8_t tRLOH_in_ns;
+ int8_t tRHOH_in_ns;
+};
+
+/**
+ * struct gpmi_nfc_hardware_timing - GPMI NFC hardware timing parameters.
+ *
+ * This structure contains timing information expressed in a form directly
+ * usable by the GPMI NFC hardware.
+ *
+ * @data_setup_in_cycles: The data setup time, in cycles.
+ * @data_hold_in_cycles: The data hold time, in cycles.
+ * @address_setup_in_cycles: The address setup time, in cycles.
+ * @use_half_periods: Indicates the clock is running slowly, so the
+ * NFC DLL should use half-periods.
+ * @sample_delay_factor: The sample delay factor.
+ */
+
+struct gpmi_nfc_hardware_timing {
+ uint8_t data_setup_in_cycles;
+ uint8_t data_hold_in_cycles;
+ uint8_t address_setup_in_cycles;
+ bool use_half_periods;
+ uint8_t sample_delay_factor;
+};
+
+/**
+ * struct nfc_hal - GPMI NFC HAL
+ *
+ * This structure embodies an abstract interface to the underlying NFC hardware.
+ *
+ * @version: The NFC hardware version.
+ * @description: A pointer to a human-readable description of
+ * the NFC hardware.
+ * @max_chip_count: The maximum number of chips the NFC can
+ * possibly support (this value is a constant for
+ * each NFC version). This may *not* be the actual
+ * number of chips connected.
+ * @max_data_setup_cycles: The maximum number of data setup cycles that
+ * can be expressed in the hardware.
+ * @internal_data_setup_in_ns: The time, in ns, that the NFC hardware requires
+ * for data read internal setup. In the Reference
+ * Manual, see the chapter "High-Speed NAND
+ * Timing" for more details.
+ * @max_sample_delay_factor: The maximum sample delay factor that can be
+ * expressed in the hardware.
+ * @max_dll_clock_period_in_ns: The maximum period of the GPMI clock that the
+ * sample delay DLL hardware can possibly work
+ * with (the DLL is unusable with longer periods).
+ * If the full-cycle period is greater than HALF
+ * this value, the DLL must be configured to use
+ * half-periods.
+ * @max_dll_delay_in_ns: The maximum amount of delay, in ns, that the
+ * DLL can implement.
+ * @dma_descriptors: A pool of DMA descriptors.
+ * @isr_dma_channel: The DMA channel with which the NFC HAL is
+ * working. We record this here so the ISR knows
+ * which DMA channel to acknowledge.
+ * @dma_done: The completion structure used for DMA
+ * interrupts.
+ * @bch_done: The completion structure used for BCH
+ * interrupts.
+ * @timing: The current timing configuration.
+ * @clock_frequency_in_hz: The clock frequency, in Hz, during the current
+ * I/O transaction. If no I/O transaction is in
+ * progress, this is the clock frequency during
+ * the most recent I/O transaction.
+ * @hardware_timing: The hardware timing configuration in effect
+ * during the current I/O transaction. If no I/O
+ * transaction is in progress, this is the
+ * hardware timing configuration during the most
+ * recent I/O transaction.
+ * @init: Initializes the NFC hardware and 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.
+ * @set_geometry: Configures the NFC hardware and data structures
+ * to match the physical NAND Flash geometry.
+ * @set_geometry: Configures the NFC hardware and data structures
+ * to match the physical NAND Flash geometry.
+ * @set_timing: Configures the NFC hardware and data structures
+ * to match the given NAND Flash bus timing.
+ * @get_timing: Returns the the clock frequency, in Hz, and
+ * the hardware timing configuration during the
+ * current I/O transaction. If no I/O transaction
+ * is in progress, this is the timing state during
+ * the most recent I/O transaction.
+ * @exit: Shuts down the NFC hardware and data
+ * structures. This function will be called after
+ * the platform has shut down off-chip
+ * communication but while communication with the
+ * NFC itself still works.
+ * @clear_bch: Clears a BCH interrupt (intended to be called
+ * by a more general interrupt handler to do
+ * device-specific clearing).
+ * @is_ready: Returns true if the given chip is ready.
+ * @begin: Begins an interaction with the NFC. This
+ * function must be called before *any* of the
+ * following functions so the NFC can prepare
+ * itself.
+ * @end: Ends interaction with the NFC. This function
+ * should be called to give the NFC a chance to,
+ * among other things, enter a lower-power state.
+ * @send_command: Sends the given buffer of command bytes.
+ * @send_data: Sends the given buffer of data bytes.
+ * @read_data: Reads data bytes into the given buffer.
+ * @send_page: Sends the given given data and OOB bytes,
+ * using the ECC engine.
+ * @read_page: Reads a page through the ECC engine and
+ * delivers the data and OOB bytes to the given
+ * buffers.
+ */
+
+#define NFC_DMA_DESCRIPTOR_COUNT (4)
+
+struct nfc_hal {
+
+ /* Hardware attributes. */
+
+ const unsigned int version;
+ const char *description;
+ const unsigned int max_chip_count;
+ const unsigned int max_data_setup_cycles;
+ const unsigned int internal_data_setup_in_ns;
+ const unsigned int max_sample_delay_factor;
+ const unsigned int max_dll_clock_period_in_ns;
+ const unsigned int max_dll_delay_in_ns;
+
+ /* Working variables. */
+
+ struct mxs_dma_desc *dma_descriptors[NFC_DMA_DESCRIPTOR_COUNT];
+ int isr_dma_channel;
+ struct completion dma_done;
+ struct completion bch_done;
+ struct gpmi_nfc_timing timing;
+ unsigned long clock_frequency_in_hz;
+
+ /* Configuration functions. */
+
+ int (*init) (struct gpmi_nfc_data *);
+ int (*extra_init) (struct gpmi_nfc_data *);
+ int (*set_geometry)(struct gpmi_nfc_data *);
+ int (*set_timing) (struct gpmi_nfc_data *,
+ const struct gpmi_nfc_timing *);
+ void (*get_timing) (struct gpmi_nfc_data *,
+ unsigned long *clock_frequency_in_hz,
+ struct gpmi_nfc_hardware_timing *);
+ void (*exit) (struct gpmi_nfc_data *);
+
+ /* Call these functions to begin and end I/O. */
+
+ void (*begin) (struct gpmi_nfc_data *);
+ void (*end) (struct gpmi_nfc_data *);
+
+ /* Call these I/O functions only between begin() and end(). */
+
+ void (*clear_bch) (struct gpmi_nfc_data *);
+ int (*is_ready) (struct gpmi_nfc_data *, unsigned chip);
+ int (*send_command)(struct gpmi_nfc_data *, unsigned chip,
+ dma_addr_t buffer, unsigned length);
+ int (*send_data) (struct gpmi_nfc_data *, unsigned chip,
+ dma_addr_t buffer, unsigned length);
+ int (*read_data) (struct gpmi_nfc_data *, unsigned chip,
+ dma_addr_t buffer, unsigned length);
+ int (*send_page) (struct gpmi_nfc_data *, unsigned chip,
+ dma_addr_t payload, dma_addr_t auxiliary);
+ int (*read_page) (struct gpmi_nfc_data *, unsigned chip,
+ dma_addr_t payload, dma_addr_t auxiliary);
+};
+
+/**
+ * struct boot_rom_helper - Boot ROM Helper
+ *
+ * This structure embodies the interface to an object that assists the driver
+ * in making decisions that relate to the Boot ROM.
+ *
+ * @version: The Boot ROM version.
+ * @description: A pointer to a human-readable description of the
+ * Boot ROM.
+ * @swap_block_mark: Indicates that the Boot ROM will swap the block
+ * mark with the first byte of the OOB.
+ * @set_geometry: Configures the Boot ROM geometry.
+ * @rom_extra_init: Arch-specific init.
+ */
+
+struct boot_rom_helper {
+ const unsigned int version;
+ const char *description;
+ const int swap_block_mark;
+ int (*set_geometry) (struct gpmi_nfc_data *);
+ int (*rom_extra_init) (struct gpmi_nfc_data *);
+};
+
+/*
+ *------------------------------------------------------------------------------
+ * External Symbols
+ *------------------------------------------------------------------------------
+ */
+
+/* NFC HAL Common Services */
+
+extern irqreturn_t gpmi_nfc_bch_isr(int irq, void *cookie);
+extern irqreturn_t gpmi_nfc_dma_isr(int irq, void *cookie);
+extern int gpmi_nfc_dma_init(struct gpmi_nfc_data *this);
+extern void gpmi_nfc_dma_exit(struct gpmi_nfc_data *this);
+extern int gpmi_nfc_set_geometry(struct gpmi_nfc_data *this);
+extern int gpmi_nfc_dma_go(struct gpmi_nfc_data *this, int dma_channel);
+extern int gpmi_nfc_compute_hardware_timing(struct gpmi_nfc_data *this,
+ struct gpmi_nfc_hardware_timing *hw);
+extern int start_dma_with_bch_irq(struct gpmi_nfc_data *this, int dma_channel);
+extern int start_dma_without_bch_irq(struct gpmi_nfc_data *this,
+ int dma_channel);
+
+/* NFC HAL Structures */
+
+extern struct nfc_hal gpmi_nfc_hal_v0;
+extern struct nfc_hal gpmi_nfc_hal_v1;
+extern struct nfc_hal gpmi_nfc_hal_v2;
+
+/* Boot ROM Helper Common Services */
+
+extern int gpmi_nfc_rom_helper_set_geometry(struct gpmi_nfc_data *this);
+
+/* Boot ROM Helper Structures */
+
+extern struct boot_rom_helper gpmi_nfc_boot_rom_helper_v0;
+extern struct boot_rom_helper gpmi_nfc_boot_rom_helper_v1;
+
+/* MTD Interface Layer */
+
+extern int gpmi_nfc_mil_init(struct gpmi_nfc_data *this);
+extern void gpmi_nfc_mil_exit(struct gpmi_nfc_data *this);
+
+#endif
diff --git a/drivers/mtd/nand/imx_nfc.c b/drivers/mtd/nand/imx_nfc.c
new file mode 100644
index 000000000000..065ff338b06d
--- /dev/null
+++ b/drivers/mtd/nand/imx_nfc.c
@@ -0,0 +1,8286 @@
+/*
+ * Copyright 2009-2010 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/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 88785032322a..66f38d598e13 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Sascha Hauer, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -30,6 +30,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/fsl_devices.h>
#include <asm/mach/flash.h>
#include <mach/mxc_nand.h>
diff --git a/drivers/mtd/nand/mxc_nd.c b/drivers/mtd/nand/mxc_nd.c
new file mode 100644
index 000000000000..7344bafa77fa
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd.c
@@ -0,0 +1,1413 @@
+/*
+ * Copyright 2004-2010 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;
+#else
+static int hardware_ecc = 1;
+#endif
+
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+static int Ecc_disabled;
+#endif
+
+static int is2k_Pagesize;
+
+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, lastErrSpare; /* 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..a2a257c3897c
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2004-2010 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..6b362ce05bb3
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd2.c
@@ -0,0 +1,1756 @@
+/*
+ * Copyright 2004-2010 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/io.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 "mxc_nd2.h"
+#include "nand_device_info.h"
+
+#define DVR_VER "3.0"
+
+/* Global address Variables */
+static void __iomem *nfc_axi_base, *nfc_ip_base;
+static int nfc_irq;
+
+struct mxc_mtd_s {
+ struct mtd_info mtd;
+ struct nand_chip nand;
+ struct mtd_partition *parts;
+ struct device *dev;
+ int disable_bi_swap; /* disable bi swap */
+ int ignore_bad_block; /* ignore bad block marker */
+ void *saved_bbt;
+ int (*saved_block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
+ int clk_active;
+};
+
+static struct mxc_mtd_s *mxc_nand_data;
+
+/*
+ * Define delay timeout value
+ */
+#define TROP_US_DELAY (1000 * 1000)
+
+struct nand_info {
+ bool bStatusRequest;
+ u16 colAddr;
+};
+
+static struct nand_info g_nandfc_info;
+
+#ifdef CONFIG_MTD_NAND_MXC_SWECC
+static int hardware_ecc;
+#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 mxc_nand_bi_swap(struct mtd_info *mtd)
+{
+ u16 ma, sa, nma, nsa;
+
+ if (!IS_LARGE_PAGE_NAND)
+ return;
+
+ /* Disable bi swap if the user set disable_bi_swap at sys entry */
+ if (mxc_nand_data->disable_bi_swap)
+ return;
+
+ ma = __raw_readw(BAD_BLK_MARKER_MAIN);
+ sa = __raw_readw(BAD_BLK_MARKER_SP);
+
+ nma = (ma & 0xFF00) | (sa >> 8);
+ nsa = (sa & 0x00FF) | (ma << 8);
+
+ __raw_writew(nma, BAD_BLK_MARKER_MAIN);
+ __raw_writew(nsa, BAD_BLK_MARKER_SP);
+}
+
+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 interrupt */
+ raw_write(raw_read(REG_NFC_INTRRUPT) & ~NFC_INT_MSK,
+ REG_NFC_INTRRUPT);
+ if (!wait_event_timeout(irq_waitq,
+ (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT),
+ msecs_to_jiffies(TROP_US_DELAY / 1000)) > 0) {
+ /* disable interrupt */
+ raw_write(raw_read(REG_NFC_INTRRUPT)
+ | NFC_INT_MSK, REG_NFC_INTRRUPT);
+
+ printk(KERN_WARNING "%s(%d): INT not set\n",
+ __func__, __LINE__);
+ return;
+ }
+ }
+ 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) {
+ printk(KERN_WARNING "%s(%d): INT not set\n",
+ __func__, __LINE__);
+ break;
+ }
+ }
+ }
+}
+
+static inline void send_atomic_cmd(u16 cmd, bool useirq)
+{
+ /* fill command */
+ raw_write(cmd, REG_NFC_FLASH_CMD);
+
+ /* 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 = NFC_GET_NFC_ACTIVE_CS();
+
+ 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);
+ mxc_nand_bi_swap(mtd);
+
+ /* update the value */
+ dbuf += dlen;
+ obuf += olen;
+
+ NFC_SET_RBA(0);
+ 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, true);
+ 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);
+ raw_write(NFC_AUTO_READ, REG_NFC_OPS);
+ wait_op_done(TROP_US_DELAY, true);
+
+ /* check ecc error */
+ mxc_check_ecc_status(mtd);
+
+ /* data transfer */
+ mxc_nand_bi_swap(mtd);
+ 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++);
+ 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);
+ break;
+ case NAND_CMD_STATUS:
+ send_atomic_cmd(cmd, useirq);
+ 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);
+
+ /* 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);
+
+ /* 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, true);
+#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);
+
+ /* 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, true);
+#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);
+
+ /* 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;
+
+ /* use atomic mode to read status instead
+ of using auto mode,auto-mode has issues
+ and the status is not correct.
+ */
+ raw_write(NFC_STATUS, REG_NFC_OPS);
+
+ wait_op_done(TROP_US_DELAY, true);
+
+ *status = (raw_read(NFC_CONFIG1) & mask) >> 16;
+
+}
+#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;
+ int cs = NFC_GET_NFC_ACTIVE_CS();
+
+ 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;
+ }
+
+ /* Restore active CS */
+ NFC_SET_NFC_ACTIVE_CS(cs);
+
+ return status;
+#else
+ volatile u16 *mainBuf = MAIN_AREA1;
+ u8 val = 1;
+ u16 ret;
+
+ /* Set ram buffer id */
+ NFC_SET_RBA(val);
+
+ /* Read status into main buffer */
+ raw_write(NFC_STATUS, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, true);
+
+ /* 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 = 0xf;
+
+ 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 == ecc_bit_mask) {
+ mtd->ecc_stats.failed++;
+ printk(KERN_WARNING "UnCorrectable RS-ECC Error\n");
+ return -1;
+ } else {
+ ret += err;
+ }
+ ecc_stat >>= 4;
+ } while (--no_subpages);
+
+ pr_debug("Correctable ECC Error(%d)\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 will enable NFC clock
+ *
+ */
+static inline void mxc_nand_clk_enable(void)
+{
+ if (!mxc_nand_data->clk_active) {
+ clk_enable(nfc_clk);
+ mxc_nand_data->clk_active = 1;
+ }
+}
+
+/*!
+ * This function will disable NFC clock
+ *
+ */
+static inline void mxc_nand_clk_disable(void)
+{
+ if (mxc_nand_data->clk_active) {
+ clk_disable(nfc_clk);
+ mxc_nand_data->clk_active = 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 */
+ mxc_nand_clk_disable();
+
+ break;
+ case 0 ... NFC_GET_MAXCHIP_SP():
+ /* Enable the NFC clock */
+ mxc_nand_clk_enable();
+ 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, true);
+ if (IS_2K_PAGE_NAND) {
+ /* another col addr cycle for 2k page */
+ send_addr((column >> 8) & 0xF, true);
+ } else if (IS_4K_PAGE_NAND) {
+ /* another col addr cycle for 4k page */
+ send_addr((column >> 8) & 0x1F, true);
+ }
+ }
+ if (page_addr != -1) {
+ do {
+ send_addr((page_addr & 0xff), true);
+ 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 = 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;
+
+ /*
+ * 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);
+ mxc_nand_bi_swap(mtd);
+#endif
+
+ if (IS_LARGE_PAGE_NAND)
+ PROG_PAGE();
+ else
+ send_prog_page(0);
+
+ break;
+
+ case NAND_CMD_ERASE1:
+ break;
+ case NAND_CMD_ERASE2:
+ 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, true);
+ /* 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
+ */
+ mxc_nand_bi_swap(mtd);
+ 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, int page)
+{
+
+#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);
+
+}
+
+/*!
+ * mxc_nand_block_bad - Claims all blocks are good
+ * In principle, this function is *only* called when the NAND Flash MTD system
+ * isn't allowed to keep an in-memory bad block table, so it is forced to ask
+ * the driver for bad block information.
+ *
+ * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so
+ * this function is *only* called when we take it away.
+ *
+ * We take away the in-memory BBT when the user sets the "ignorebad" parameter,
+ * which indicates that all blocks should be reported good.
+ *
+ * Thus, this function is only called when we want *all* blocks to look good,
+ * so it *always* return success.
+ *
+ * @mtd: Ignored.
+ * @ofs: Ignored.
+ * @getchip: Ignored.
+ */
+static int mxc_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ return 0;
+}
+
+/* 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;
+
+ 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;
+
+ /* fix up the offset */
+ largepage_memorybased.offs = BAD_BLK_MARKER_OOB_OFFS;
+
+ /* keep compatible for bbt table with old soc */
+ if (cpu_is_mx53()) {
+ bbt_mirror_descr.offs = BAD_BLK_MARKER_OOB_OFFS + 2;
+ bbt_main_descr.offs = BAD_BLK_MARKER_OOB_OFFS + 2;
+ bbt_mirror_descr.veroffs = bbt_mirror_descr.offs + 4;
+ bbt_main_descr.veroffs = bbt_main_descr.offs + 4;
+ }
+
+ /* 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 int mxc_get_resources(struct platform_device *pdev)
+{
+ struct resource *r;
+ int error = 0;
+
+#define MXC_NFC_NO_IP_REG \
+ (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx32() || cpu_is_mx35())
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ error = -ENXIO;
+ goto out_0;
+ }
+ nfc_axi_base = ioremap(r->start, resource_size(r));
+
+ if (!MXC_NFC_NO_IP_REG) {
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r) {
+ error = -ENXIO;
+ goto out_1;
+ }
+ }
+ nfc_ip_base = ioremap(r->start, resource_size(r));
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ error = -ENXIO;
+ goto out_2;
+ }
+ nfc_irq = r->start;
+
+ init_waitqueue_head(&irq_waitq);
+ error = request_irq(nfc_irq, mxc_nfc_irq, 0, "mxc_nd", NULL);
+ if (error)
+ goto out_3;
+
+ return 0;
+out_3:
+out_2:
+ if (!MXC_NFC_NO_IP_REG)
+ iounmap(nfc_ip_base);
+out_1:
+ iounmap(nfc_axi_base);
+out_0:
+ return error;
+}
+
+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);
+
+ if (!(cpu_is_mx53())) {
+ /* 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);
+}
+
+int nand_scan_mid(struct mtd_info *mtd)
+{
+ int i;
+ uint8_t id_bytes[NAND_DEVICE_ID_BYTE_COUNT];
+ struct nand_chip *this = mtd->priv;
+ struct nand_device_info *dev_info;
+
+ if (!IS_LARGE_PAGE_NAND)
+ return 0;
+
+ /* Read ID bytes from the first NAND Flash chip. */
+ this->select_chip(mtd, 0);
+
+ this->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ for (i = 0; i < NAND_DEVICE_ID_BYTE_COUNT; i++)
+ id_bytes[i] = this->read_byte(mtd);
+
+ /* Get information about this device, based on the ID bytes. */
+ dev_info = nand_device_get_info(id_bytes);
+
+ /* Check if we understand this device. */
+ if (!dev_info) {
+ printk(KERN_ERR "Unrecognized NAND Flash device.\n");
+ return !0;
+ }
+
+ /* Correct mtd setting */
+ this->chipsize = dev_info->chip_size_in_bytes;
+ mtd->size = dev_info->chip_size_in_bytes * this->numchips;
+ mtd->writesize = dev_info->page_total_size_in_bytes & ~0x3ff;
+ mtd->oobsize = dev_info->page_total_size_in_bytes & 0x3ff;
+ mtd->erasesize = dev_info->block_size_in_pages * mtd->writesize;
+
+ /* Calculate the address shift from the page size */
+ this->page_shift = ffs(mtd->writesize) - 1;
+ /* Convert chipsize to number of pages per chip -1. */
+ this->pagemask = (this->chipsize >> this->page_shift) - 1;
+
+ this->bbt_erase_shift = this->phys_erase_shift =
+ ffs(mtd->erasesize) - 1;
+ this->chip_shift = ffs(this->chipsize) - 1;
+
+ return 0;
+}
+
+/*!
+ * show_device_disable_bi_swap()
+ * Shows the value of the 'disable_bi_swap' 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_disable_bi_swap(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", mxc_nand_data->disable_bi_swap);
+}
+
+/*!
+ * store_device_disable_bi_swap()
+ * Sets the value of the 'disable_bi_swap' 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_disable_bi_swap(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ const char *p = buf;
+ unsigned long v;
+
+ /* Try to make sense of what arrived from user space. */
+
+ if (strict_strtoul(p, 0, &v) < 0)
+ return size;
+
+ if (v > 0)
+ v = 1;
+ mxc_nand_data->disable_bi_swap = v;
+ return size;
+
+}
+
+static DEVICE_ATTR(disable_bi_swap, 0644,
+ show_device_disable_bi_swap, store_device_disable_bi_swap);
+
+/*!
+ * show_device_ignorebad()
+ * Shows the value of the 'ignore_bad_block' 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_ignorebad(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", mxc_nand_data->ignore_bad_block);
+}
+
+/*!
+ * store_device_ignorebad()
+ * Sets the value of the 'ignore_bad_block' 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_ignorebad(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ const char *p = buf;
+ unsigned long v;
+
+ /* Try to make sense of what arrived from user space. */
+
+ if (strict_strtoul(p, 0, &v) < 0)
+ return size;
+
+ if (v > 0)
+ v = 1;
+
+ /* Only do something if the value is changing. */
+ if (v != mxc_nand_data->ignore_bad_block) {
+ if (v) {
+ /*
+ * If control arrives here, we want to begin ignoring
+ * bad block marks. Reach into the NAND Flash MTD data
+ * structures and set the in-memory BBT pointer to NULL.
+ * This will cause the NAND Flash MTD code to believe
+ * that it never created a BBT and force it to call our
+ * block_bad function.
+ */
+ mxc_nand_data->saved_bbt = mxc_nand_data->nand.bbt;
+ mxc_nand_data->nand.bbt = 0;
+ mxc_nand_data->saved_block_bad =
+ mxc_nand_data->nand.block_bad;
+ mxc_nand_data->nand.block_bad =
+ mxc_nand_block_bad;
+ } else {
+ /*
+ * If control arrives here, we want to stop ignoring
+ * bad block marks. Restore the NAND Flash MTD's pointer
+ * to its in-memory BBT.
+ */
+ mxc_nand_data->nand.bbt = mxc_nand_data->saved_bbt;
+ mxc_nand_data->nand.block_bad =
+ mxc_nand_data->saved_block_bad;
+ }
+ mxc_nand_data->ignore_bad_block = v;
+ }
+
+ return size;
+
+}
+
+static DEVICE_ATTR(ignorebad, 0644,
+ show_device_ignorebad, store_device_ignorebad);
+
+
+static struct device_attribute *device_attributes[] = {
+ &dev_attr_disable_bi_swap,
+ &dev_attr_ignorebad,
+};
+/*!
+ * manage_sysfs_files() - Creates/removes sysfs files for this device.
+ *
+ * @create: create/remove the sys entry.
+ */
+static void manage_sysfs_files(int create)
+{
+ struct device *dev = mxc_nand_data->dev;
+ 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(dev, *attr);
+ if (error) {
+ while (--attr >= device_attributes)
+ device_remove_file(dev, *attr);
+ return;
+ }
+ } else {
+ device_remove_file(dev, *attr);
+ }
+ }
+
+}
+
+
+/*!
+ * 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 __devinit 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;
+
+ /* get the resource */
+ err = mxc_get_resources(pdev);
+ if (err)
+ goto out;
+
+ /* 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);
+ mxc_nand_data->clk_active = 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_ident(mtd, NFC_GET_MAXCHIP_SP(), NULL)
+ || nand_scan_mid(mtd)
+ || nand_scan_tail(mtd)) {
+ 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);
+ }
+
+ /* Create sysfs entries for this device. */
+ manage_sysfs_files(true);
+
+ 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();
+
+ manage_sysfs_files(false);
+ mxc_free_buf();
+
+ mxc_nand_clk_disable();
+ clk_put(nfc_clk);
+ platform_set_drvdata(pdev, NULL);
+
+ if (mxc_nand_data) {
+ nand_release(mtd);
+ free_irq(nfc_irq, 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)
+{
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND2 : NAND suspend\n");
+
+ /* Disable the NFC clock */
+ mxc_nand_clk_disable();
+
+ return 0;
+}
+
+/*!
+ * 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)
+{
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND2 : NAND resume\n");
+
+ /* Enable the NFC clock */
+ mxc_nand_clk_enable();
+
+ return 0;
+}
+
+#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..4120c2aa1722
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd2.h
@@ -0,0 +1,718 @@
+/*
+ * Copyright 2004-2010 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 GET_NAND_PAGE_SIZE (mtd->writesize / num_of_interleave)
+
+#define NAND_PAGESIZE_2KB 2048
+#define NAND_PAGESIZE_4KB 4096
+
+/*
+ * main area for bad block marker is in the last data section
+ * the spare area for swapped bad block marker is the second
+ * byte of last spare section
+ */
+#define NAND_SECTIONS (GET_NAND_PAGE_SIZE >> 9)
+#define NAND_OOB_PER_SECTION (((GET_NAND_OOB_SIZE / NAND_SECTIONS) >> 1) << 1)
+#define NAND_CHUNKS (GET_NAND_PAGE_SIZE / (512 + NAND_OOB_PER_SECTION))
+
+#define BAD_BLK_MARKER_MAIN_OFFS \
+ (GET_NAND_PAGE_SIZE - NAND_CHUNKS * NAND_OOB_PER_SECTION)
+
+#define BAD_BLK_MARKER_SP_OFFS (NAND_CHUNKS * SPARE_LEN)
+
+#define BAD_BLK_MARKER_OOB_OFFS (NAND_CHUNKS * NAND_OOB_PER_SECTION)
+
+#define BAD_BLK_MARKER_MAIN \
+ ((u32)MAIN_AREA0 + BAD_BLK_MARKER_MAIN_OFFS)
+
+#define BAD_BLK_MARKER_SP \
+ ((u32)SPARE_AREA0 + BAD_BLK_MARKER_SP_OFFS)
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3
+/*
+ * For V3 NFC registers Definition
+ */
+
+#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 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_mx53() > 0) { \
+ if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ ~(3 << 6)) | \
+ NFC_ECC_MODE_16), NFC_CONFIG2); \
+ else \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ ~(3 << 6)) | \
+ NFC_ECC_MODE_4), NFC_CONFIG2); \
+ } else 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) & \
+ ~(1 << 6)) | \
+ NFC_ECC_MODE_8), NFC_CONFIG2); \
+ else \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ ~(1 << 6)) | \
+ NFC_ECC_MODE_4), NFC_CONFIG2); \
+ } else { \
+ if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ ~(1 << 6)) | \
+ NFC_ECC_MODE_4), NFC_CONFIG2); \
+ else \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ ~(1 << 6)) | \
+ NFC_ECC_MODE_8), NFC_CONFIG2); \
+ } \
+} while (0)
+
+#define WRITE_NFC_IP_REG(val, reg) \
+ do { \
+ raw_write(raw_read(NFC_IPC) | NFC_IPC_CREQ, NFC_IPC); \
+ while (!(raw_read(NFC_IPC) & NFC_IPC_ACK)) \
+ ; \
+ raw_write(val, reg); \
+ raw_write(raw_read(NFC_IPC) & ~NFC_IPC_CREQ, NFC_IPC); \
+ } while (0)
+
+#else
+#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))
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define NFC_BLS_LOCKED (0 << 6)
+#define NFC_BLS_LOCKED_DEFAULT (1 << 6)
+#define NFC_BLS_UNLCOKED (2 << 6)
+#define NFC_BLS_RESET (~(3 << 6))
+#else
+#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))
+#endif
+
+#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 (0x0 << 6)
+#define NFC_ECC_MODE_8 (0x1 << 6)
+#define NFC_ECC_MODE_16 (0x3 << 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)
+
+/* 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_NFC_ACTIVE_CS() \
+ ((raw_read(NFC_CONFIG1) >> NFC_ACTIVE_CS_SHIFT) & \
+ ((1 << NFC_ACTIVE_CS_WIDTH) - 1))
+
+#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_NFC_ACTIVE_CS()
+#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) | NFC_NO_SDMA, NFC_CONFIG3); \
+ raw_write(raw_read(NFC_CONFIG3) | NFC_RBB_MODE, NFC_CONFIG3); \
+ if (cpu_is_mx51()) \
+ 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
+ */
+
+/*
+ * Addresses for NFC registers
+ */
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_REG_BASE (nfc_axi_base + 0x1000)
+#else
+#define NFC_REG_BASE nfc_axi_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_axi_base + 0x000)
+#define MAIN_AREA1 (u16 *)(nfc_axi_base + 0x200)
+
+/*!
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define SPARE_AREA0 (u16 *)(nfc_axi_base + 0x1000)
+#define SPARE_LEN 64
+#define SPARE_COUNT 8
+#else
+#define SPARE_AREA0 (u16 *)(nfc_axi_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 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 { \
+ if (((v) & (1 << NFMS_NF_PG_SZ))) { \
+ if (IS_2K_PAGE_NAND) { \
+ (NFMS |= 0x00000100); \
+ (NFMS &= ~0x00000200); \
+ NFC_SET_SPAS(NFC_SPAS_64); \
+ } else if (IS_4K_PAGE_NAND) { \
+ (NFMS &= ~0x00000100); \
+ (NFMS |= 0x00000200); \
+ GET_NAND_OOB_SIZE == 128 ? \
+ NFC_SET_SPAS(NFC_SPAS_128) : \
+ NFC_SET_SPAS(NFC_SPAS_218); \
+ } else { \
+ printk(KERN_ERR "Err for setting page/oob size"); \
+ } \
+ NFC_SET_ECC_MODE(GET_NAND_OOB_SIZE >> 1); \
+ } \
+} while (0)
+#else
+#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_NFC_ACTIVE_CS()
+#define NFC_GET_MAXCHIP_SP() 1
+#define NFC_SET_WPC(val) val
+
+#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__ */
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 4a7b86423ee9..3a8216bbf838 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1424,7 +1424,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
*
* Internal function. Called with chip held.
*/
-static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int chipnr, page, realpage, col, bytes, aligned;
@@ -1553,6 +1553,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}
+EXPORT_SYMBOL(nand_do_read_ops);
/**
* nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
@@ -2133,7 +2134,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
*
* NAND write with ECC
*/
-static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
int chipnr, realpage, page, blockmask, column;
@@ -2237,6 +2238,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
ops->oobretlen = ops->ooblen;
return ret;
}
+EXPORT_SYMBOL(nand_do_write_ops);
/**
* panic_nand_write - [MTD Interface] NAND write with ECC
@@ -2584,6 +2586,12 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
instr->state = MTD_ERASE_FAILED;
instr->fail_addr =
((loff_t)page << chip->page_shift);
+
+ if (chip->bbt) {
+ int i = (page / pages_per_block) << 1;
+ chip->bbt[i >> 3] |= 0x03 << (i & 0x6);
+ mtd->ecc_stats.badblocks++;
+ }
goto erase_exit;
}
@@ -2649,6 +2657,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/* Return more or less happy */
return ret;
}
+EXPORT_SYMBOL_GPL(nand_erase_nand);
/**
* nand_sync - [MTD Interface] sync
@@ -2834,8 +2843,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->chipsize = (uint64_t)type->chipsize << 20;
- /* Newer devices have all the information in additional id bytes */
- if (!type->pagesize) {
+ if (!type->pagesize && chip->init_size) {
+ /* set the pagesize, oobsize, erasesize by the driver*/
+ busw = chip->init_size(mtd, chip, id_data);
+ } else if (!type->pagesize) {
int extid;
/* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = id_data[2];
diff --git a/drivers/mtd/nand/nand_device_info.c b/drivers/mtd/nand/nand_device_info.c
new file mode 100644
index 000000000000..6a23810ebadb
--- /dev/null
+++ b/drivers/mtd/nand/nand_device_info.c
@@ -0,0 +1,2413 @@
+/*
+ * Copyright (C) 2009-2010 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 <asm/sizes.h>
+#include <linux/mtd/nand.h>
+
+#include "nand_device_info.h"
+
+/*
+ * Type 2
+ */
+static struct nand_device_info nand_device_info_table_type_2[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x20,
+ .device_code = 0xf1,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 128LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 30,
+ .data_hold_in_ns = 20,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "NAND01GW3",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xf1,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 128LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xf1,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 128LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 30,
+ .data_hold_in_ns = 20,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xf1,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 128LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 35,
+ .data_hold_in_ns = 25,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9F1F08",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xf1,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 128LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 30,
+ .data_hold_in_ns = 20,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TC58NVG0S3",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x45,
+ .device_code = 0xf1,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 128LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 32,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x20,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "NAND02GW3",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 30,
+ .data_hold_in_ns = 25,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "HY27UF082G2M, HY27UG082G2M, HY27UG082G1M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F2G08",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9F2G08U0M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TC58NVG1S3",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x45,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 32,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x20,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 10,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "HY27UH084G2M, HY27UG084G2M, HY27UH084G1M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F4G08",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 25,
+ .data_hold_in_ns = 25,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 25,
+ .data_hold_in_ns = 25,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TH58NVG2S3",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x45,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 32,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 30,
+ .data_hold_in_ns = 25,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "HY27UH088G2M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x20,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "NAND08GW3BxANx",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 25,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F8G08FABWG",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 32,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x20,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 25,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 32,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0x48,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ /* TODO: The actual oob size for MT29F16G08ABACA is
+ 224 bytes. Use oob 218 bytes since MX53 NFC controller
+ mentions the spare area size must be less or equal 218
+ byte if ECC is enabled */
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "MT29F16G08ABACA(2GB)",
+ },
+ {true}
+};
+
+/*
+ * Large MLC
+ */
+static struct nand_device_info nand_device_info_table_large_mlc[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TC58NVG1D4BFT00",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x45,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x45,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 35,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TH58NVG3D4xFT00",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x45,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 35,
+ .data_hold_in_ns = 20,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 35,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TH58NVG4D4xFT00",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x45,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 35,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 0,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TC58NVG2D4BFT00",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 25,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9G4G08U0M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 45,
+ .data_hold_in_ns = 25,
+ .address_setup_in_ns = 50,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "HY27UT084G2M, HY27UU088G5M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x20,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 40,
+ .data_hold_in_ns = 20,
+ .address_setup_in_ns = 30,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "NAND04GW3C2AN1E",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9G8G08U0M, K9HAG08U1M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 60,
+ .data_hold_in_ns = 30,
+ .address_setup_in_ns = 50,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "HY27UV08AG5M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "Intel JS29F08G08AAMiB1 and Micron MT29F8G08MAA; "
+ "Intel JS29F08G08CAMiB1 and Micron MT29F16G08QAA",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9LAG08U0M K9HBG08U1M K9GAG08U0M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "Intel JS29F32G08FAMiB1 and Micron MT29F32G08TAA",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 20,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F4G08",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x89,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "JS29F08G08AAMiB2, JS29F08G08CAMiB2",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x89,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "JS29F32G08FAMiB2",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "HY27UW08CGFM",
+ },
+ {true}
+};
+
+/*
+ * Type 7
+ */
+static struct nand_device_info nand_device_info_table_type_7[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 25,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F8G08FABWG",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F4G08AAA",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xdc,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 512LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 12,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9F4G08",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 25,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 35,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9K8G08UXM, K9NBG08U5A, K9WAG08U1A",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 12,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9WAG08UXM",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xda,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 256LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9F2G08U0A",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xf1,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 128LL*SZ_1M,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 2*SZ_1K + 64,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 12,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9F1F08",
+ },
+ {true}
+};
+
+/*
+ * Type 8
+ */
+static struct nand_device_info nand_device_info_table_type_8[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 128,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9GAG08U0M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 128,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9LBG08U0M (32Gb), K9HCG08U1M (64Gb), K9MDG08U5M (128Gb)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 128,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 20,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 0,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "H27UAG, H27UBG",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 128,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 23,
+ .data_hold_in_ns = 20,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 0,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "H27UCG",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 8LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 128,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "H27U8G8T2B",
+ },
+ {true}
+};
+
+/*
+ * Type 9
+ */
+static struct nand_device_info nand_device_info_table_type_9[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TC58NVG3D1DTG00",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TC58NVG4D1DTG00",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 10,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "TH58NVG6D1DTG20",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x89,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 10,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "JS29F16G08AAMC1, JS29F32G08CAMC1",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F16G08MAA, MT29F32G08QAA",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F64G08TAA (32Gb), MT29F32G08CBAAA (32Gb) MT29F64G08CFAAA (64Gb)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd9,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 8LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 10,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "MT29F128G08CJAAA",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x89,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 10,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "JSF64G08FAMC1",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "K9LBG08U0D",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 8,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9GAG08U0D, K9LBG08U1D, K9HCG08U5D",
+ },
+ {true}
+};
+
+/*
+ * Type 10
+ */
+static struct nand_device_info nand_device_info_table_type_10[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd3,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 1LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 4*SZ_1K + 128,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 4*SZ_1K + 128,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 25,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 30,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ "K9NCG08U5M",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_SLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 64,
+ .page_total_size_in_bytes = 4*SZ_1K + 128,
+ .ecc_strength_in_bits = 4,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 15,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = -1,
+ .tRLOH_in_ns = -1,
+ .tRHOH_in_ns = -1,
+ NULL,
+ },
+ {true}
+};
+
+/*
+ * Type 11
+ */
+static struct nand_device_info nand_device_info_table_type_11[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 8*SZ_1K + 376,
+ .ecc_strength_in_bits = 14,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 8,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 25,
+ "TC58NVG5D2ELAM8 (4GB), TH58NVG6D2ELAM8 (8GB)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x98,
+ .device_code = 0xde,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 8LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 8*SZ_1K + 376,
+ .ecc_strength_in_bits = 14,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 8,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 25,
+ "TH58NVG7D2ELAM8",
+ },
+ {true}
+};
+
+/*
+ * Type 15
+ */
+static struct nand_device_info nand_device_info_table_type_15[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 8*SZ_1K + 436,
+ .ecc_strength_in_bits = 16,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 25,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "K9GBG08U0M (4GB, 1CE); K9LCG08U1M (8GB, 2CE); K9HDG08U5M (16GB, 4CE)",
+ },
+ {true}
+};
+
+static struct nand_device_info nand_device_info_table_type_16[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xec,
+ .device_code = 0xd7,
+ .is_ddr_ok = true,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 8*SZ_1K + 512,
+ .ecc_strength_in_bits = 24,
+ .ecc_size_in_bytes = 1024,
+ .data_setup_in_ns = 20,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 25,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 25,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "K9LCGD88X1M(8GB, 1CE); K9HDGD8X5M(8GB, 2CE); K9PFGD8X7M(16GB, 4CE)",
+ },
+ {true}
+};
+/*
+ * BCH ECC12
+ */
+static struct nand_device_info nand_device_info_table_bch_ecc12[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd5,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 224,
+ .ecc_strength_in_bits = 12,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "H27UAG8T2ATR (2GB, 1CE)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 224,
+ .ecc_strength_in_bits = 12,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "H27UBG8T2M (4GB, 1CE), H27UCG8UDM (8GB, 2CE), H27UDG8VEM (16GB, 4CE)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0xad,
+ .device_code = 0xde,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 8LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 224,
+ .ecc_strength_in_bits = 12,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "H27UEG8YEM (32GB, 4CE)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd7,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 12,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 10,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 16,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "MT29F32G08CBAAA (4GB, 1CE), MT29F64G08CFAAA (8GB, 2CE)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0xd9,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 8LL*SZ_1G,
+ .block_size_in_pages = 128,
+ .page_total_size_in_bytes = 4*SZ_1K + 218,
+ .ecc_strength_in_bits = 12,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 10,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 15,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 16,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "MT29F128G08CJAAA (16GB, 2CE)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0x48,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 2LL*SZ_1G,
+ .block_size_in_pages = 256,
+ .page_total_size_in_bytes = 4*SZ_1K + 224,
+ .ecc_strength_in_bits = 12,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "MT29F16G08CBABA (2GB, 1CE)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0x68,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 4LL*SZ_1G,
+ .block_size_in_pages = 256,
+ .page_total_size_in_bytes = 4*SZ_1K + 224,
+ .ecc_strength_in_bits = 12,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "MT29F32G08CBABA (4GB, 1CE); "
+ "MT29F64G08CEABA (8GB, 2CE); "
+ "MT29F64G08CFABA (8GB, 2CE)",
+ },
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0x88,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 8LL*SZ_1G,
+ .block_size_in_pages = 256,
+ .page_total_size_in_bytes = 4*SZ_1K + 224,
+ .ecc_strength_in_bits = 12,
+ .ecc_size_in_bytes = 512,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "MT29F128G08CJABA (16GB, 2CE); "
+ "MT29F128G08CKABA (16GB, 2CE); "
+ "MT29F256G08CUABA (32GB, 4CE)",
+ },
+ {true}
+};
+
+/*
+ * BCH ECC24
+ */
+static struct nand_device_info nand_device_info_table_bch_ecc24[] __initdata = {
+ {
+ .end_of_table = false,
+ .manufacturer_code = 0x2c,
+ .device_code = 0x88,
+ .is_ddr_ok = true,
+ .cell_technology = NAND_DEVICE_CELL_TECH_MLC,
+ .chip_size_in_bytes = 8LL * SZ_1G,
+ .block_size_in_pages = 256,
+ .page_total_size_in_bytes = 8 * SZ_1K + 448,
+ .ecc_strength_in_bits = 24,
+ .ecc_size_in_bytes = 1024,
+ .data_setup_in_ns = 15,
+ .data_hold_in_ns = 10,
+ .address_setup_in_ns = 20,
+ .gpmi_sample_delay_in_ns = 6,
+ .tREA_in_ns = 20,
+ .tRLOH_in_ns = 5,
+ .tRHOH_in_ns = 15,
+ "MT29F64G08CBAAA(8GB, 2CE) ",
+ },
+ {true}
+};
+
+/*
+ * The following macros make it convenient to extract information from an ID
+ * byte array. All these macros begin with the prefix "ID_".
+ *
+ * Macros of the form:
+ *
+ * ID_GET_[<manufacturer>_[<modifier>_]]<field>
+ *
+ * extract the given field from an ID byte array. Macros of the form:
+ *
+ * ID_[<manufacturer>_[<modifier>_]]<field>_<meaning>
+ *
+ * contain the value for the given field that has the given meaning.
+ *
+ * If the <manufacturer> appears, it means this macro represents a view of this
+ * field that is specific to the given manufacturer.
+ *
+ * If the <modifier> appears, it means this macro represents a view of this
+ * field that the given manufacturer applies only under specific conditions.
+ *
+ * Here is a simple example:
+ *
+ * ID_PAGE_SIZE_CODE_2K
+ *
+ * This macro has the value of the "Page Size" field that indicates the page
+ * size is 2K.
+ *
+ * A more complicated example:
+ *
+ * ID_SAMSUNG_6_BYTE_PAGE_SIZE_CODE_8K (0x2)
+ *
+ * This macro has the value of the "Page Size" field for Samsung parts that
+ * indicates the page size is 8K. However, this interpretation is only correct
+ * for devices that return 6 ID bytes.
+ */
+
+/* Byte 1 ------------------------------------------------------------------- */
+
+#define ID_GET_BYTE_1(id) ((id)[0])
+
+#define ID_GET_MFR_CODE(id) ID_GET_BYTE_1(id)
+
+/* Byte 2 ------------------------------------------------------------------- */
+
+#define ID_GET_BYTE_2(id) ((id)[1])
+
+#define ID_GET_DEVICE_CODE(id) ID_GET_BYTE_2(id)
+ #define ID_SAMSUNG_DEVICE_CODE_1_GBIT (0xf1)
+ #define ID_SAMSUNG_DEVICE_CODE_2_GBIT (0xda)
+ #define ID_HYNIX_DEVICE_CODE_ECC12 (0xd7)
+ #define ID_HYNIX_DEVICE_CODE_ECC12_ODD (0xd5)
+ #define ID_HYNIX_DEVICE_CODE_ECC12_LARGE (0xde)
+ #define ID_MICRON_DEVICE_CODE_ECC12 (0xd7) /* ECC12 */
+ #define ID_MICRON_DEVICE_CODE_ECC12_LARGE (0xd9) /* ECC12 8GB/CE */
+ #define ID_MICRON_DEVICE_CODE_ECC12_2GB_PER_CE (0x48) /* L63B 2GB/CE */
+ #define ID_MICRON_DEVICE_CODE_ECC12_4GB_PER_CE (0x68) /* L63B 4GB/CE */
+ #define ID_MICRON_DEVICE_CODE_ECC12_8GB_PER_CE (0x88) /* L63B 8GB/CE */
+ #define ID_MICRON_DEVICE_CODE_ECC24_8GB_PER_CE (0x88) /* L63B 8GB/CE */
+
+/* Byte 3 ------------------------------------------------------------------- */
+
+#define ID_GET_BYTE_3(id) ((id)[2])
+
+#define ID_GET_DIE_COUNT_CODE(id) ((ID_GET_BYTE_3(id) >> 0) & 0x3)
+
+#define ID_GET_CELL_TYPE_CODE(id) ((ID_GET_BYTE_3(id) >> 2) & 0x3)
+ #define ID_CELL_TYPE_CODE_SLC (0x0) /* All others => MLC. */
+
+#define ID_GET_SAMSUNG_SIMUL_PROG(id) ((ID_GET_BYTE_3(id) >> 4) & 0x3)
+
+#define ID_GET_MICRON_SIMUL_PROG(id) ((ID_GET_BYTE_3(id) >> 4) & 0x3)
+
+#define ID_GET_CACHE_PROGRAM(id) ((ID_GET_BYTE_3(id) >> 7) & 0x1)
+
+#define ID_ONFI_NAND_BYTE3 (0x04)
+/* Byte 4 ------------------------------------------------------------------- */
+
+#define ID_GET_BYTE_4(id) ((id)[3])
+ #define ID_HYNIX_BYTE_4_ECC12_DEVICE (0x25)
+
+#define ID_GET_PAGE_SIZE_CODE(id) ((ID_GET_BYTE_4(id) >> 0) & 0x3)
+ #define ID_PAGE_SIZE_CODE_1K (0x0)
+ #define ID_PAGE_SIZE_CODE_2K (0x1)
+ #define ID_PAGE_SIZE_CODE_4K (0x2)
+ #define ID_PAGE_SIZE_CODE_8K (0x3)
+ #define ID_SAMSUNG_6_BYTE_PAGE_SIZE_CODE_8K (0x2)
+
+#define ID_GET_OOB_SIZE_CODE(id) ((ID_GET_BYTE_4(id) >> 2) & 0x1)
+
+#define ID_GET_BLOCK_SIZE_CODE(id) ((ID_GET_BYTE_4(id) >> 4) & 0x3)
+
+/* Byte 5 ------------------------------------------------------------------- */
+
+#define ID_GET_BYTE_5(id) ((id)[4])
+ #define ID_MICRON_BYTE_5_ECC12 (0x84)
+
+#define ID_GET_SAMSUNG_ECC_LEVEL_CODE(id) ((ID_GET_BYTE_5(id) >> 4) & 0x7)
+ #define ID_SAMSUNG_ECC_LEVEL_CODE_8 (0x03)
+ #define ID_SAMSUNG_ECC_LEVEL_CODE_24 (0x05)
+
+#define ID_GET_PLANE_COUNT_CODE(id) ((ID_GET_BYTE_5(id) >> 2) & 0x3)
+
+/* Byte 6 ------------------------------------------------------------------- */
+
+#define ID_GET_BYTE_6(id) ((id)[5])
+ #define ID_TOSHIBA_BYTE_6_PAGE_SIZE_CODE_8K (0x54)
+ #define ID_TOSHIBA_BYTE_6_PAGE_SIZE_CODE_4K (0x13)
+
+#define ID_GET_SAMSUNG_DEVICE_VERSION_CODE(id) ((ID_GET_BYTE_6(id)>>0) & 0x7)
+ #define ID_SAMSUNG_DEVICE_VERSION_CODE_40NM (0x01)
+
+#define ID_GET_TOGGLE_NAND(id) ((ID_GET_BYTE_6(id) >> 7) & 0x1)
+
+/* -------------------------------------------------------------------------- */
+
+void nand_device_print_info(struct nand_device_info *info)
+{
+ unsigned i;
+ const char *mfr_name;
+ const char *cell_technology_name;
+ uint64_t chip_size;
+ const char *chip_size_units;
+ unsigned page_data_size_in_bytes;
+ unsigned page_oob_size_in_bytes;
+
+ /* Check for nonsense. */
+
+ if (!info)
+ return;
+
+ /* Prepare the manufacturer name. */
+
+ mfr_name = "Unknown";
+
+ for (i = 0; nand_manuf_ids[i].id; i++) {
+ if (nand_manuf_ids[i].id == info->manufacturer_code) {
+ mfr_name = nand_manuf_ids[i].name;
+ break;
+ }
+ }
+
+ /* Prepare the name of the cell technology. */
+
+ switch (info->cell_technology) {
+ case NAND_DEVICE_CELL_TECH_SLC:
+ cell_technology_name = "SLC";
+ break;
+ case NAND_DEVICE_CELL_TECH_MLC:
+ cell_technology_name = "MLC";
+ break;
+ default:
+ cell_technology_name = "Unknown";
+ break;
+ }
+
+ /* Prepare the chip size. */
+
+ if ((info->chip_size_in_bytes >= SZ_1G) &&
+ !(info->chip_size_in_bytes % SZ_1G)) {
+ chip_size = info->chip_size_in_bytes / ((uint64_t) SZ_1G);
+ chip_size_units = "GiB";
+ } else if ((info->chip_size_in_bytes >= SZ_1M) &&
+ !(info->chip_size_in_bytes % SZ_1M)) {
+ chip_size = info->chip_size_in_bytes / ((uint64_t) SZ_1M);
+ chip_size_units = "MiB";
+ } else {
+ chip_size = info->chip_size_in_bytes;
+ chip_size_units = "B";
+ }
+
+ /* Prepare the page geometry. */
+
+ page_data_size_in_bytes = (1<<(fls(info->page_total_size_in_bytes)-1));
+ page_oob_size_in_bytes = info->page_total_size_in_bytes -
+ page_data_size_in_bytes;
+
+ /* Print the information. */
+
+ printk(KERN_INFO "Manufacturer : %s (0x%02x)\n", mfr_name,
+ info->manufacturer_code);
+ printk(KERN_INFO "Device Code : 0x%02x\n", info->device_code);
+ printk(KERN_INFO "Cell Technology : %s\n", cell_technology_name);
+ printk(KERN_INFO "Chip Size : %llu %s\n", chip_size,
+ chip_size_units);
+ printk(KERN_INFO "Pages per Block : %u\n",
+ info->block_size_in_pages);
+ printk(KERN_INFO "Page Geometry : %u+%u\n", page_data_size_in_bytes,
+ page_oob_size_in_bytes);
+ printk(KERN_INFO "ECC Strength : %u bits\n",
+ info->ecc_strength_in_bits);
+ printk(KERN_INFO "ECC Size : %u B\n", info->ecc_size_in_bytes);
+ printk(KERN_INFO "Data Setup Time : %u ns\n", info->data_setup_in_ns);
+ printk(KERN_INFO "Data Hold Time : %u ns\n", info->data_hold_in_ns);
+ printk(KERN_INFO "Address Setup Time: %u ns\n",
+ info->address_setup_in_ns);
+ printk(KERN_INFO "GPMI Sample Delay : %u ns\n",
+ info->gpmi_sample_delay_in_ns);
+ if (info->tREA_in_ns >= 0)
+ printk(KERN_INFO "tREA : %u ns\n",
+ info->tREA_in_ns);
+ else
+ printk(KERN_INFO "tREA : Unknown\n");
+ if (info->tREA_in_ns >= 0)
+ printk(KERN_INFO "tRLOH : %u ns\n",
+ info->tRLOH_in_ns);
+ else
+ printk(KERN_INFO "tRLOH : Unknown\n");
+ if (info->tREA_in_ns >= 0)
+ printk(KERN_INFO "tRHOH : %u ns\n",
+ info->tRHOH_in_ns);
+ else
+ printk(KERN_INFO "tRHOH : Unknown\n");
+ if (info->description)
+ printk(KERN_INFO "Description : %s\n", info->description);
+ else
+ printk(KERN_INFO "Description : <None>\n");
+
+}
+
+static struct nand_device_info *nand_device_info_search(
+ struct nand_device_info *table, uint8_t mfr_code, uint8_t device_code)
+{
+
+ for (; !table->end_of_table; table++) {
+ if (table->manufacturer_code != mfr_code)
+ continue;
+ if (table->device_code != device_code)
+ continue;
+ return table;
+ }
+
+ return 0;
+
+}
+
+static struct nand_device_info * __init nand_device_info_fn_toshiba(const uint8_t id[])
+{
+ struct nand_device_info *table;
+
+ /* Check for an SLC device. */
+
+ if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) {
+ /* Type 2 */
+ return nand_device_info_search(nand_device_info_table_type_2,
+ ID_GET_MFR_CODE(id), ID_GET_DEVICE_CODE(id));
+ }
+
+ /*
+ * Look for 8K page Toshiba MLC devices.
+ *
+ * The page size field in byte 4 can't be used because the field was
+ * redefined in the 8K parts so the value meaning "8K page" is the same
+ * as the value meaning "4K page" on the 4K page devices.
+ *
+ * The only identifiable difference between the 4K and 8K page Toshiba
+ * devices with a device code of 0xd7 is the undocumented 6th ID byte.
+ * The 4K device returns a value of 0x13 and the 8K a value of 0x54.
+ * Toshiba has verified that this is an acceptable method to distinguish
+ * the two device families.
+ */
+
+ if (ID_GET_BYTE_6(id) == ID_TOSHIBA_BYTE_6_PAGE_SIZE_CODE_8K) {
+ /* Type 11 */
+ table = nand_device_info_table_type_11;
+ } else if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) {
+ /* Type 9 */
+ table = nand_device_info_table_type_9;
+ } else {
+ /* Large MLC */
+ table = nand_device_info_table_large_mlc;
+ }
+
+ return nand_device_info_search(table, ID_GET_MFR_CODE(id),
+ ID_GET_DEVICE_CODE(id));
+
+}
+
+static struct nand_device_info * __init nand_device_info_fn_samsung(const uint8_t id[])
+{
+ struct nand_device_info *table;
+
+ /* Check for an MLC device. */
+
+ if (ID_GET_CELL_TYPE_CODE(id) != ID_CELL_TYPE_CODE_SLC) {
+
+ /* Is this a Samsung 8K Page MLC device with 16 bit ECC? */
+ if ((ID_GET_SAMSUNG_ECC_LEVEL_CODE(id) ==
+ ID_SAMSUNG_ECC_LEVEL_CODE_24) &&
+ (ID_GET_PAGE_SIZE_CODE(id) ==
+ ID_SAMSUNG_6_BYTE_PAGE_SIZE_CODE_8K)) {
+ if (ID_GET_TOGGLE_NAND(id))
+ table = nand_device_info_table_type_16;
+ else
+ /* Type 15 */
+ table = nand_device_info_table_type_15;
+ }
+ /* Is this a Samsung 42nm ECC8 device with a 6 byte ID? */
+ else if ((ID_GET_SAMSUNG_ECC_LEVEL_CODE(id) ==
+ ID_SAMSUNG_ECC_LEVEL_CODE_8) &&
+ (ID_GET_SAMSUNG_DEVICE_VERSION_CODE(id) ==
+ ID_SAMSUNG_DEVICE_VERSION_CODE_40NM)) {
+ /* Type 9 */
+ table = nand_device_info_table_type_9;
+ } else if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) {
+ /* Type 8 */
+ table = nand_device_info_table_type_8;
+ } else {
+ /* Large MLC */
+ table = nand_device_info_table_large_mlc;
+ }
+
+ } else {
+
+ /* Check the page size first. */
+ if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) {
+ /* Type 10 */
+ table = nand_device_info_table_type_10;
+ }
+ /* Check the chip size. */
+ else if (ID_GET_DEVICE_CODE(id) ==
+ ID_SAMSUNG_DEVICE_CODE_1_GBIT) {
+ if (!ID_GET_CACHE_PROGRAM(id)) {
+ /*
+ * 128 MiB Samsung chips without cache program
+ * are Type 7.
+ *
+ * The K9F1G08U0B does not support multi-plane
+ * program, so the if statement below cannot be
+ * used to identify it.
+ */
+ table = nand_device_info_table_type_7;
+
+ } else {
+ /* Smaller sizes are Type 2 by default. */
+ table = nand_device_info_table_type_2;
+ }
+ } else {
+ /* Check number of simultaneously programmed pages. */
+ if (ID_GET_SAMSUNG_SIMUL_PROG(id) &&
+ ID_GET_PLANE_COUNT_CODE(id)) {
+ /* Type 7 */
+ table = nand_device_info_table_type_7;
+ } else {
+ /* Type 2 */
+ table = nand_device_info_table_type_2;
+ }
+
+ }
+
+ }
+
+ return nand_device_info_search(table, ID_GET_MFR_CODE(id),
+ ID_GET_DEVICE_CODE(id));
+
+}
+
+static struct nand_device_info * __init nand_device_info_fn_stmicro(const uint8_t id[])
+{
+ struct nand_device_info *table;
+
+ /* Check for an SLC device. */
+
+ if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC)
+ /* Type 2 */
+ table = nand_device_info_table_type_2;
+ else
+ /* Large MLC */
+ table = nand_device_info_table_large_mlc;
+
+ return nand_device_info_search(table, ID_GET_MFR_CODE(id),
+ ID_GET_DEVICE_CODE(id));
+
+}
+
+static struct nand_device_info * __init nand_device_info_fn_hynix(const uint8_t id[])
+{
+ struct nand_device_info *table;
+
+ /* Check for an SLC device. */
+
+ if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) {
+ /* Type 2 */
+ return nand_device_info_search(nand_device_info_table_type_2,
+ ID_GET_MFR_CODE(id), ID_GET_DEVICE_CODE(id));
+ }
+
+ /*
+ * Check for ECC12 devices.
+ *
+ * We look at the 4th ID byte to distinguish some Hynix ECC12 devices
+ * from the similar ECC8 part. For example H27UBG8T2M (ECC12) 4th byte
+ * is 0x25, whereas H27UDG8WFM (ECC8) 4th byte is 0xB6.
+ */
+
+ if ((((ID_GET_DEVICE_CODE(id) == ID_HYNIX_DEVICE_CODE_ECC12) ||
+ (ID_GET_DEVICE_CODE(id) == ID_HYNIX_DEVICE_CODE_ECC12_ODD)) &&
+ ID_GET_BYTE_4(id) == ID_HYNIX_BYTE_4_ECC12_DEVICE) ||
+ (ID_GET_DEVICE_CODE(id) == ID_HYNIX_DEVICE_CODE_ECC12_LARGE)) {
+ /* BCH ECC 12 */
+ table = nand_device_info_table_bch_ecc12;
+ } else if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) {
+ /*
+ * So far, all other Samsung and Hynix 4K page devices are
+ * Type 8.
+ */
+ table = nand_device_info_table_type_8;
+ } else
+ /* Large MLC */
+ table = nand_device_info_table_large_mlc;
+
+ return nand_device_info_search(table, ID_GET_MFR_CODE(id),
+ ID_GET_DEVICE_CODE(id));
+
+}
+
+static struct nand_device_info * __init nand_device_info_fn_micron(const uint8_t id[])
+{
+ struct nand_device_info *table;
+
+ /* Check for an SLC device. */
+
+ if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) {
+
+ /* Check number of simultaneously programmed pages. */
+
+ if (ID_GET_MICRON_SIMUL_PROG(id)) {
+ /* Type 7 */
+ table = nand_device_info_table_type_7;
+ } else {
+ /* Zero simultaneously programmed pages means Type 2. */
+ table = nand_device_info_table_type_2;
+ }
+
+ return nand_device_info_search(table, ID_GET_MFR_CODE(id),
+ ID_GET_DEVICE_CODE(id));
+
+ }
+
+ if (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC24_8GB_PER_CE
+ && ID_GET_BYTE_3(id) == ID_ONFI_NAND_BYTE3) {
+ /* BCH ECC 24 */
+ table = nand_device_info_table_bch_ecc24;
+ } else
+ /*
+ * We look at the 5th ID byte to distinguish some Micron ECC12 NANDs
+ * from the similar ECC8 part.
+ *
+ * For example MT29F64G08CFAAA (ECC12) 5th byte is 0x84, whereas
+ * MT29F64G08TAA (ECC8) 5th byte is 0x78.
+ *
+ * We also have a special case for the Micron L63B family
+ * (256 page/block), which has unique device codes but no ID fields that
+ * can easily be used to distinguish the family.
+ */
+
+ if ((ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12 &&
+ ID_GET_BYTE_5(id) == ID_MICRON_BYTE_5_ECC12) ||
+ (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12_LARGE) ||
+ (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12_2GB_PER_CE) ||
+ (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12_4GB_PER_CE) ||
+ (ID_GET_DEVICE_CODE(id) == ID_MICRON_DEVICE_CODE_ECC12_8GB_PER_CE)) {
+ /* BCH ECC 12 */
+ table = nand_device_info_table_bch_ecc12;
+ } else if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) {
+ /* Toshiba devices with 4K pages are Type 9. */
+ table = nand_device_info_table_type_9;
+ } else {
+ /* Large MLC */
+ table = nand_device_info_table_large_mlc;
+ }
+
+ return nand_device_info_search(table, ID_GET_MFR_CODE(id),
+ ID_GET_DEVICE_CODE(id));
+
+}
+
+static struct nand_device_info * __init nand_device_info_fn_sandisk(const uint8_t id[])
+{
+ struct nand_device_info *table;
+
+ if (ID_GET_CELL_TYPE_CODE(id) != ID_CELL_TYPE_CODE_SLC) {
+ /* Large MLC */
+ table = nand_device_info_table_large_mlc;
+ } else {
+ /* Type 2 */
+ table = nand_device_info_table_type_2;
+ }
+
+ return nand_device_info_search(table, ID_GET_MFR_CODE(id),
+ ID_GET_DEVICE_CODE(id));
+
+}
+
+static struct nand_device_info * __init nand_device_info_fn_intel(const uint8_t id[])
+{
+ struct nand_device_info *table;
+
+ /* Check for an SLC device. */
+
+ if (ID_GET_CELL_TYPE_CODE(id) == ID_CELL_TYPE_CODE_SLC) {
+ /* Type 2 */
+ return nand_device_info_search(nand_device_info_table_type_2,
+ ID_GET_MFR_CODE(id), ID_GET_DEVICE_CODE(id));
+ }
+
+ if (ID_GET_PAGE_SIZE_CODE(id) == ID_PAGE_SIZE_CODE_4K) {
+ /* Type 9 */
+ table = nand_device_info_table_type_9;
+ } else {
+ /* Large MLC */
+ table = nand_device_info_table_large_mlc;
+ }
+
+ return nand_device_info_search(table, ID_GET_MFR_CODE(id),
+ ID_GET_DEVICE_CODE(id));
+
+}
+
+/**
+ * struct nand_device_type_info - Information about a NAND Flash type.
+ *
+ * @name: A human-readable name for this type.
+ * @table: The device info table for this type.
+ */
+
+struct nand_device_type_info {
+ struct nand_device_info *table;
+ const char *name;
+};
+
+/*
+ * A table that maps manufacturer IDs to device information tables.
+ */
+
+static struct nand_device_type_info nand_device_type_directory[] __initdata = {
+ {nand_device_info_table_type_2, "Type 2" },
+ {nand_device_info_table_large_mlc, "Large MLC"},
+ {nand_device_info_table_type_7, "Type 7" },
+ {nand_device_info_table_type_8, "Type 8" },
+ {nand_device_info_table_type_9, "Type 9" },
+ {nand_device_info_table_type_10, "Type 10" },
+ {nand_device_info_table_type_11, "Type 11" },
+ {nand_device_info_table_type_15, "Type 15" },
+ {nand_device_info_table_type_16, "Type 16" },
+ {nand_device_info_table_bch_ecc12, "BCH ECC12"},
+ {nand_device_info_table_bch_ecc24, "BCH ECC24"},
+ {0, 0},
+};
+
+/**
+ * struct nand_device_mfr_info - Information about a NAND Flash manufacturer.
+ *
+ * @id: The value of the first NAND Flash ID byte, which identifies the
+ * manufacturer.
+ * @fn: A pointer to a function to use for identifying devices from the
+ * given manufacturer.
+ */
+
+struct nand_device_mfr_info {
+ uint8_t id;
+ struct nand_device_info *(*fn)(const uint8_t id[]);
+};
+
+/*
+ * A table that maps manufacturer IDs to device information tables.
+ */
+
+static struct nand_device_mfr_info nand_device_mfr_directory[] __initdata = {
+ {
+ .id = NAND_MFR_TOSHIBA,
+ .fn = nand_device_info_fn_toshiba,
+ },
+ {
+ .id = NAND_MFR_SAMSUNG,
+ .fn = nand_device_info_fn_samsung,
+ },
+ {
+ .id = NAND_MFR_FUJITSU,
+ .fn = 0,
+ },
+ {
+ .id = NAND_MFR_NATIONAL,
+ .fn = 0,
+ },
+ {
+ .id = NAND_MFR_RENESAS,
+ .fn = 0,
+ },
+ {
+ .id = NAND_MFR_STMICRO,
+ .fn = nand_device_info_fn_stmicro,
+ },
+ {
+ .id = NAND_MFR_HYNIX,
+ .fn = nand_device_info_fn_hynix,
+ },
+ {
+ .id = NAND_MFR_MICRON,
+ .fn = nand_device_info_fn_micron,
+ },
+ {
+ .id = NAND_MFR_AMD,
+ .fn = 0,
+ },
+ {
+ .id = NAND_MFR_SANDISK,
+ .fn = nand_device_info_fn_sandisk,
+ },
+ {
+ .id = NAND_MFR_INTEL,
+ .fn = nand_device_info_fn_intel,
+ },
+ {0, 0}
+};
+
+/**
+ * nand_device_info_test_table - Validate a device info table.
+ *
+ * This function runs tests on the given device info table to check that it
+ * meets the current assumptions.
+ */
+
+static void __init nand_device_info_test_table(
+ struct nand_device_info *table, const char *name)
+{
+ unsigned i;
+ unsigned j;
+ uint8_t mfr_code;
+ uint8_t device_code;
+
+ /* Loop over entries in this table. */
+
+ for (i = 0; !table[i].end_of_table; i++) {
+
+ /* Get discriminating attributes of the current device. */
+
+ mfr_code = table[i].manufacturer_code;
+ device_code = table[i].device_code;
+
+ /* Compare with the remaining devices in this table. */
+
+ for (j = i + 1; !table[j].end_of_table; j++) {
+ if ((mfr_code == table[j].manufacturer_code) &&
+ (device_code == table[j].device_code))
+ goto error;
+ }
+
+ }
+
+ return;
+
+error:
+
+ printk(KERN_EMERG
+ "\n== NAND Flash device info table failed validity check ==\n");
+
+ printk(KERN_EMERG "\nDevice Info Table: %s\n", name);
+ printk(KERN_EMERG "\nTable Index %u\n", i);
+ nand_device_print_info(table + i);
+ printk(KERN_EMERG "\nTable Index %u\n", j);
+ nand_device_print_info(table + j);
+ printk(KERN_EMERG "\n");
+
+ BUG();
+
+}
+
+/**
+ * nand_device_info_test_data - Test the NAND Flash device data.
+ */
+
+static void __init nand_device_info_test_data(void)
+{
+
+ unsigned i;
+
+ for (i = 0; nand_device_type_directory[i].name; i++) {
+ nand_device_info_test_table(
+ nand_device_type_directory[i].table,
+ nand_device_type_directory[i].name);
+ }
+
+}
+
+struct nand_device_info * __init nand_device_get_info(const uint8_t id[])
+{
+ unsigned i;
+ uint8_t mfr_id = ID_GET_MFR_CODE(id);
+ struct nand_device_info *(*fn)(const uint8_t id[]) = 0;
+
+ /* Test the data. */
+
+ nand_device_info_test_data();
+
+ /* Look for information about this manufacturer. */
+
+ for (i = 0; nand_device_mfr_directory[i].id; i++) {
+ if (nand_device_mfr_directory[i].id == mfr_id) {
+ fn = nand_device_mfr_directory[i].fn;
+ break;
+ }
+ }
+
+ if (!fn)
+ return 0;
+
+ /*
+ * If control arrives here, we found both a table of device information,
+ * and a function we can use to identify the current device. Attempt to
+ * identify the device and return the result.
+ */
+
+ return fn(id);
+
+}
diff --git a/drivers/mtd/nand/nand_device_info.h b/drivers/mtd/nand/nand_device_info.h
new file mode 100644
index 000000000000..f74c975b2fb6
--- /dev/null
+++ b/drivers/mtd/nand/nand_device_info.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2009-2010 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
+ */
+#ifndef __DRIVERS_NAND_DEVICE_INFO_H
+#define __DRIVERS_NAND_DEVICE_INFO_H
+
+ /*
+ * The number of ID bytes to read from the NAND Flash device and hand over to
+ * the identification system.
+ */
+
+#define NAND_DEVICE_ID_BYTE_COUNT (6)
+
+ /*
+ * The number of ID bytes to read from the NAND Flash device and hand over to
+ * the identification system.
+ */
+
+enum nand_device_cell_technology {
+ NAND_DEVICE_CELL_TECH_SLC = 0,
+ NAND_DEVICE_CELL_TECH_MLC = 1,
+};
+
+/**
+ * struct nand_device_info - Information about a single NAND Flash device.
+ *
+ * This structure contains all the *essential* information about a NAND Flash
+ * device, derived from the device's data sheet. For each manufacturer, we have
+ * an array of these structures.
+ *
+ * @end_of_table: If true, marks the end of a table of device
+ * information.
+ * @manufacturer_code: The manufacturer code (1st ID byte) reported by
+ * the device.
+ * @device_code: The device code (2nd ID byte) reported by the
+ * device.
+ * @is_ddr_ok: Is this nand an ONFI nand or TOGGLE nand ?
+ * @cell_technology: The storage cell technology.
+ * @chip_size_in_bytes: The total size of the storage behind a single
+ * chip select, in bytes. Notice that this is *not*
+ * necessarily the total size of the storage in a
+ * *package*, which may contain several chips.
+ * @block_size_in_pages: The number of pages in a block.
+ * @page_total_size_in_bytes: The total size of a page, in bytes, including
+ * both the data and the OOB.
+ * @ecc_strength_in_bits: The strength of the ECC called for by the
+ * manufacturer, in number of correctable bits.
+ * @ecc_size_in_bytes: The size of the data block over which the
+ * manufacturer calls for the given ECC algorithm
+ * and strength.
+ * @data_setup_in_ns: The data setup time, in nanoseconds. Usually the
+ * maximum of tDS and tWP. A negative value
+ * indicates this characteristic isn't known.
+ * @data_hold_in_ns: The data hold time, in nanoseconds. Usually the
+ * maximum of tDH, tWH and tREH. A negative value
+ * indicates this characteristic isn't known.
+ * @address_setup_in_ns: The address setup time, in nanoseconds. Usually
+ * the maximum of tCLS, tCS and tALS. A negative
+ * value indicates this characteristic isn't known.
+ * @gpmi_sample_delay_in_ns: A GPMI-specific timing parameter. A negative
+ * value indicates this characteristic isn't known.
+ * @tREA_in_ns: tREA, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic
+ * isn't known.
+ * @tRLOH_in_ns: tRLOH, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic
+ * isn't known.
+ * @tRHOH_in_ns: tRHOH, in nanoseconds, from the data sheet. A
+ * negative value indicates this characteristic
+ * isn't known.
+ */
+
+struct nand_device_info {
+
+ /* End of table marker */
+
+ bool end_of_table;
+
+ /* Manufacturer and Device codes */
+
+ uint8_t manufacturer_code;
+ uint8_t device_code;
+
+ /* Does the nand support DDR? (ONFI or TOGGLE) */
+ bool is_ddr_ok;
+
+ /* Technology */
+
+ enum nand_device_cell_technology cell_technology;
+
+ /* Geometry */
+
+ uint64_t chip_size_in_bytes;
+ uint32_t block_size_in_pages;
+ uint16_t page_total_size_in_bytes;
+
+ /* ECC */
+
+ uint8_t ecc_strength_in_bits;
+ uint16_t ecc_size_in_bytes;
+
+ /* Timing */
+
+ int8_t data_setup_in_ns;
+ int8_t data_hold_in_ns;
+ int8_t address_setup_in_ns;
+ int8_t gpmi_sample_delay_in_ns;
+ int8_t tREA_in_ns;
+ int8_t tRLOH_in_ns;
+ int8_t tRHOH_in_ns;
+
+ /* Description */
+
+ const char *description;
+
+};
+
+/**
+ * nand_device_get_info - Get info about a device based on ID bytes.
+ *
+ * @id_bytes: An array of NAND_DEVICE_ID_BYTE_COUNT ID bytes retrieved from the
+ * NAND Flash device.
+ */
+
+struct nand_device_info *nand_device_get_info(const uint8_t id_bytes[]);
+
+/**
+ * nand_device_print_info - Prints information about a NAND Flash device.
+ *
+ * @info A pointer to a NAND Flash device information structure.
+ */
+
+void nand_device_print_info(struct nand_device_info *info);
+
+/*
+ * Check the NAND whether it supports the DDR mode.
+ * Only the ONFI nand and TOGGLE nand support the DDR now.
+ */
+static inline bool is_ddr_nand(struct nand_device_info *info)
+{
+ return info->is_ddr_ok;
+}
+#endif
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 89907ed99009..241c73b7328a 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -109,6 +109,10 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
+ {"NAND 2GiB 3,3V 8-bit", 0x48, 0, 2048, 0, LP_OPTIONS},
+
+ /* 32 Gigabit ,only use 2G due to the linux mtd limitation*/
+ {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 2048, 0, LP_OPTIONS},
/*
* Renesas AND 1 Gigabit. Those chips do not support extended id and
@@ -125,6 +129,7 @@ struct nand_flash_dev nand_flash_ids[] = {
BBT_AUTO_REFRESH
},
+ {"ONFI NAND 3,3V 8-bit", 0x88, 0, 2048, 0, 0 /*LP_OPTIONS*/ },
{NULL,}
};
@@ -141,6 +146,8 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"},
{NAND_MFR_AMD, "AMD"},
+ {NAND_MFR_SANDISK, "SanDisk"},
+ {NAND_MFR_INTEL, "Intel"},
{0x0, "Unknown"}
};
diff --git a/drivers/mtd/ubiblock.c b/drivers/mtd/ubiblock.c
new file mode 100644
index 000000000000..d71c1f800d09
--- /dev/null
+++ b/drivers/mtd/ubiblock.c
@@ -0,0 +1,626 @@
+/*
+ * Direct UBI block device access
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * 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
+ *
+ * Based on mtdblock by:
+ * (C) 2000-2003 Nicolas Pitre <nico@cam.org>
+ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/genhd.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/blktrans.h>
+#include <linux/mtd/ubi.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include "ubi/ubi.h"
+#include "mtdcore.h"
+
+static LIST_HEAD(ubiblk_devices);
+static struct mutex ubiblk_devices_lock;
+
+/**
+ * ubiblk_dev - the structure representing translation layer
+ *
+ * @m: interface to mtd_blktrans
+ * @ubi_num: UBI device number
+ * @ubi_vol: UBI volume ID
+ * @usecount: reference count
+ *
+ * @cache_mutex: protects access to cache_data
+ * @cache_data: content of the cached LEB
+ * @cache_offset: offset of cached data on UBI volume, in bytes
+ * @cache_size: cache size in bytes, usually equal to LEB size
+ */
+struct ubiblk_dev {
+ struct mtd_blktrans_dev *m;
+
+ int ubi_num;
+ int ubi_vol;
+ int usecount;
+ struct ubi_volume_desc *ubi;
+
+ struct mutex cache_mutex;
+ unsigned char *cache_data;
+ unsigned long cache_offset;
+ unsigned int cache_size;
+
+ enum {
+ STATE_EMPTY,
+ STATE_CLEAN,
+ STATE_DIRTY
+ } cache_state;
+
+ struct list_head list;
+
+ struct work_struct unbind;
+};
+
+static int ubiblock_open(struct mtd_blktrans_dev *mbd);
+static int ubiblock_release(struct mtd_blktrans_dev *mbd);
+static int ubiblock_flush(struct mtd_blktrans_dev *mbd);
+static int ubiblock_readsect(struct mtd_blktrans_dev *mbd,
+ unsigned long block, char *buf);
+static int ubiblock_writesect(struct mtd_blktrans_dev *mbd,
+ unsigned long block, char *buf);
+static int ubiblock_getgeo(struct mtd_blktrans_dev *mbd,
+ struct hd_geometry *geo);
+static void *ubiblk_add(int ubi_num, int ubi_vol_id);
+static void *ubiblk_add_locked(int ubi_num, int ubi_vol_id);
+static int ubiblk_del(struct ubiblk_dev *u);
+static int ubiblk_del_locked(struct ubiblk_dev *u);
+
+/*
+ * These two routines are just to satify mtd_blkdev's requirements
+ */
+static void ubiblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+{
+ return;
+}
+
+static void ubiblock_remove_dev(struct mtd_blktrans_dev *mbd)
+{
+ return;
+}
+
+static struct mtd_blktrans_ops ubiblock_tr = {
+
+ .name = "ubiblk",
+ .major = 0, /* assign dynamically */
+ .part_bits = 3, /* allow up to 8 parts */
+ .blksize = 512,
+
+ .open = ubiblock_open,
+ .release = ubiblock_release,
+ .flush = ubiblock_flush,
+ .readsect = ubiblock_readsect,
+ .writesect = ubiblock_writesect,
+ .getgeo = ubiblock_getgeo,
+
+ .add_mtd = ubiblock_add_mtd,
+ .remove_dev = ubiblock_remove_dev,
+ .owner = THIS_MODULE,
+};
+
+static int ubiblock_getgeo(struct mtd_blktrans_dev *bdev,
+ struct hd_geometry *geo)
+{
+ return -ENOTTY;
+}
+
+/**
+ * ubiblk_write_cached_data - flush the cache to the UBI volume
+ */
+static int ubiblk_write_cached_data(struct ubiblk_dev *u)
+{
+ int ret;
+
+ if (u->cache_state != STATE_DIRTY)
+ return 0;
+
+ pr_debug("%s: volume %d:%d, writing at %lx of size %x\n",
+ __func__, u->ubi_num, u->ubi_vol,
+ u->cache_offset, u->cache_size);
+
+ ret = ubi_write(u->ubi, u->cache_offset / u->cache_size,
+ u->cache_data, 0, u->cache_size);
+ pr_debug("leb_write status %d\n", ret);
+
+ u->cache_state = STATE_EMPTY;
+ return ret;
+}
+
+/**
+ * ubiblk_do_cached_write - cached write the data to the UBI volume
+ *
+ * @u: ubiblk_dev
+ * @pos: offset on the block device
+ * @len: buffer length
+ * @buf: buffer itself
+ *
+ * if buffer contains one or more whole sectors (=LEBs), write them to the
+ * volume. Otherwise, fill the cache that will be flushed later
+ */
+static int ubiblk_do_cached_write(struct ubiblk_dev *u, unsigned long pos,
+ int len, const char *buf)
+{
+ unsigned int sect_size = u->cache_size,
+ size, offset;
+ unsigned long sect_start;
+ int ret = 0;
+ int leb;
+
+ pr_debug("%s: volume %d:%d, writing at pos %lx of size %x\n",
+ __func__, u->ubi_num, u->ubi_vol, pos, len);
+
+ while (len > 0) {
+ leb = pos / sect_size;
+ sect_start = leb * sect_size;
+ offset = pos - sect_start;
+ size = sect_size - offset;
+
+ if (size > len)
+ size = len;
+
+ if (size == sect_size) {
+ /*
+ * We are covering a whole sector. Thus there is no
+ * need to bother with the cache while it may still be
+ * useful for other partial writes.
+ */
+ ret = ubi_leb_change(u->ubi, leb,
+ buf, size, UBI_UNKNOWN);
+ if (ret)
+ goto out;
+ } else {
+ /* Partial sector: need to use the cache */
+
+ if (u->cache_state == STATE_DIRTY &&
+ u->cache_offset != sect_start) {
+ ret = ubiblk_write_cached_data(u);
+ if (ret)
+ goto out;
+ }
+
+ if (u->cache_state == STATE_EMPTY ||
+ u->cache_offset != sect_start) {
+ /* fill the cache with the current sector */
+ u->cache_state = STATE_EMPTY;
+ ret = ubi_leb_read(u->ubi, leb,
+ u->cache_data, 0, u->cache_size, 0);
+ if (ret)
+ return ret;
+ ret = ubi_leb_unmap(u->ubi, leb);
+ if (ret)
+ return ret;
+ ret = ubi_leb_unmap(u->ubi, leb);
+ if (ret)
+ return ret;
+ u->cache_offset = sect_start;
+ u->cache_state = STATE_CLEAN;
+ }
+
+ /* write data to our local cache */
+ memcpy(u->cache_data + offset, buf, size);
+ u->cache_state = STATE_DIRTY;
+ }
+
+ buf += size;
+ pos += size;
+ len -= size;
+ }
+
+out:
+ return ret;
+}
+
+/**
+ * ubiblk_do_cached_read - cached read the data from ubi volume
+ *
+ * @u: ubiblk_dev
+ * @pos: offset on the block device
+ * @len: buffer length
+ * @buf: preallocated buffer
+ *
+ * Cached LEB will be used if possible; otherwise data will be using
+ * ubi_leb_read
+ */
+static int ubiblk_do_cached_read(struct ubiblk_dev *u, unsigned long pos,
+ int len, char *buf)
+{
+ unsigned int sect_size = u->cache_size;
+ int err = 0;
+ unsigned long sect_start;
+ unsigned offset, size;
+ int leb;
+
+ pr_debug("%s: read at 0x%lx, size 0x%x\n",
+ __func__, pos, len);
+
+ while (len > 0) {
+
+ leb = pos/sect_size;
+ sect_start = leb*sect_size;
+ offset = pos - sect_start;
+ size = sect_size - offset;
+
+ if (size > len)
+ size = len;
+
+ /*
+ * Check if the requested data is already cached
+ * Read the requested amount of data from our internal
+ * cache if it contains what we want, otherwise we read
+ * the data directly from flash.
+ */
+ if (u->cache_state != STATE_EMPTY &&
+ u->cache_offset == sect_start) {
+ pr_debug("%s: cached, returning back from cache\n",
+ __func__);
+ memcpy(buf, u->cache_data + offset, size);
+ err = 0;
+ } else {
+ pr_debug("%s: pos = %ld, reading leb = %d\n",
+ __func__, pos, leb);
+ err = ubi_leb_read(u->ubi, leb, buf, offset, size, 0);
+ if (err)
+ goto out;
+ }
+
+ buf += size;
+ pos += size;
+ len -= size;
+ }
+
+out:
+ return err;
+}
+
+static struct ubiblk_dev *ubiblk_find_by_ptr(struct mtd_blktrans_dev *m)
+{
+ struct ubiblk_dev *pos;
+
+ mutex_lock(&ubiblk_devices_lock);
+ list_for_each_entry(pos, &ubiblk_devices, list)
+ if (pos->m == m) {
+ mutex_unlock(&ubiblk_devices_lock);
+ return pos;
+ }
+ mutex_unlock(&ubiblk_devices_lock);
+ BUG();
+ return NULL;
+}
+
+/**
+ * ubiblock_writesect - write the sector
+ *
+ * Allocate the cache, if necessary and perform actual write using
+ * ubiblk_do_cached_write
+ */
+static int ubiblock_writesect(struct mtd_blktrans_dev *mbd,
+ unsigned long block, char *buf)
+{
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
+
+ if (unlikely(!u->cache_data)) {
+ u->cache_data = vmalloc(u->cache_size);
+ if (!u->cache_data)
+ return -EAGAIN;
+ }
+ return ubiblk_do_cached_write(u, block<<9, 512, buf);
+}
+
+/**
+ * ubiblk_readsect - read the sector
+ *
+ * Allocate the cache, if necessary, and perform actual read using
+ * ubiblk_do_cached_read
+ */
+static int ubiblock_readsect(struct mtd_blktrans_dev *mbd,
+ unsigned long block, char *buf)
+{
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
+
+ if (unlikely(!u->cache_data)) {
+ u->cache_data = vmalloc(u->cache_size);
+ if (!u->cache_data)
+ return -EAGAIN;
+ }
+ return ubiblk_do_cached_read(u, block<<9, 512, buf);
+}
+
+static int ubiblk_flush_locked(struct ubiblk_dev *u)
+{
+ ubiblk_write_cached_data(u);
+ ubi_sync(u->ubi_num);
+ return 0;
+}
+
+static int ubiblock_flush(struct mtd_blktrans_dev *mbd)
+{
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
+
+ mutex_lock(&u->cache_mutex);
+ ubiblk_flush_locked(u);
+ mutex_unlock(&u->cache_mutex);
+ return 0;
+}
+
+
+static int ubiblock_open(struct mtd_blktrans_dev *mbd)
+{
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
+
+ if (u->usecount == 0) {
+ u->ubi = ubi_open_volume(u->ubi_num, u->ubi_vol,
+ UBI_READWRITE);
+ if (IS_ERR(u->ubi))
+ return PTR_ERR(u->ubi);
+ }
+ u->usecount++;
+ return 0;
+}
+
+static int ubiblock_release(struct mtd_blktrans_dev *mbd)
+{
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(mbd);
+
+ if (--u->usecount == 0) {
+ mutex_lock(&u->cache_mutex);
+ ubiblk_flush_locked(u);
+ vfree(u->cache_data);
+ u->cache_data = NULL;
+ mutex_unlock(&u->cache_mutex);
+ ubi_close_volume(u->ubi);
+ u->ubi = NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * sysfs routines. The ubiblk creates two entries under /sys/block/ubiblkX:
+ * - volume, R/O, which is read like "ubi0:volume_name"
+ * - unbind, W/O; when user writes something here, the block device is
+ * removed
+ *
+ * unbind schedules a work item to perform real unbind, because sysfs entry
+ * handler cannot delete itself :)
+ */
+ssize_t volume_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gendisk *gd = dev_to_disk(dev);
+ struct mtd_blktrans_dev *m = gd->private_data;
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(m);
+
+ return sprintf(buf, "%d:%d\n", u->ubi_num, u->ubi_vol);
+}
+
+static void ubiblk_unbind(struct work_struct *ws)
+{
+ struct ubiblk_dev *u = container_of(ws, struct ubiblk_dev, unbind);
+
+ ubiblk_del(u);
+}
+
+ssize_t unbind_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gendisk *gd = dev_to_disk(dev);
+ struct mtd_blktrans_dev *m = gd->private_data;
+ struct ubiblk_dev *u = ubiblk_find_by_ptr(m);
+
+ INIT_WORK(&u->unbind, ubiblk_unbind);
+ schedule_work(&u->unbind);
+ return count;
+}
+
+DEVICE_ATTR(unbind, 0644, NULL, unbind_store);
+DEVICE_ATTR(volume, 0644, volume_show, NULL);
+
+static int ubiblk_sysfs(struct gendisk *hd, int add)
+{
+ int r = 0;
+
+ if (add) {
+ r = device_create_file(disk_to_dev(hd), &dev_attr_unbind);
+ if (r < 0)
+ goto out;
+ r = device_create_file(disk_to_dev(hd), &dev_attr_volume);
+ if (r < 0)
+ goto out1;
+ return 0;
+ }
+
+ device_remove_file(disk_to_dev(hd), &dev_attr_unbind);
+out1:
+ device_remove_file(disk_to_dev(hd), &dev_attr_volume);
+out:
+ return r;
+}
+
+
+/**
+ * add the FTL by registering it with mtd_blkdevs
+ */
+static void *ubiblk_add(int ubi_num, int ubi_vol_id)
+{
+ void *p;
+
+ mutex_lock(&mtd_table_mutex);
+ p = ubiblk_add_locked(ubi_num, ubi_vol_id);
+ mutex_unlock(&mtd_table_mutex);
+ return p;
+}
+
+static void *ubiblk_add_locked(int ubi_num, int ubi_vol_id)
+{
+ struct ubiblk_dev *u = kzalloc(sizeof(*u), GFP_KERNEL);
+ struct ubi_volume_info uvi;
+ struct ubi_volume_desc *ubi;
+
+ if (!u) {
+ u = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ u->m = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL);
+ if (!u->m) {
+ kfree(u);
+ u = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ ubi = ubi_open_volume(ubi_num, ubi_vol_id, UBI_READONLY);
+ if (IS_ERR(ubi)) {
+ pr_err("cannot open the volume\n");
+ kfree(u->m);
+ kfree(u);
+ u = (void *)ubi;
+ goto out;
+ }
+
+ ubi_get_volume_info(ubi, &uvi);
+
+ pr_debug("adding volume of size %d (used_size %lld), LEB size %d\n",
+ uvi.size, uvi.used_bytes, uvi.usable_leb_size);
+
+ u->m->mtd = ubi->vol->ubi->mtd;
+ u->m->devnum = -1;
+ u->m->size = uvi.used_bytes >> 9;
+ u->m->tr = &ubiblock_tr;
+
+ ubi_close_volume(ubi);
+
+ u->ubi_num = ubi_num;
+ u->ubi_vol = ubi_vol_id;
+
+ mutex_init(&u->cache_mutex);
+ u->cache_state = STATE_EMPTY;
+ u->cache_size = uvi.usable_leb_size;
+ u->cache_data = NULL;
+ u->usecount = 0;
+ INIT_LIST_HEAD(&u->list);
+
+ mutex_lock(&ubiblk_devices_lock);
+ list_add_tail(&u->list, &ubiblk_devices);
+ mutex_unlock(&ubiblk_devices_lock);
+ add_mtd_blktrans_dev(u->m);
+ ubiblk_sysfs(u->m->disk, true);
+out:
+ return u;
+}
+
+static int ubiblk_del(struct ubiblk_dev *u)
+{
+ int r;
+ mutex_lock(&mtd_table_mutex);
+ r = ubiblk_del_locked(u);
+ mutex_unlock(&mtd_table_mutex);
+ return r;
+}
+
+static int ubiblk_del_locked(struct ubiblk_dev *u)
+{
+ if (u->usecount != 0)
+ return -EBUSY;
+ ubiblk_sysfs(u->m->disk, false);
+ del_mtd_blktrans_dev(u->m);
+
+ mutex_lock(&ubiblk_devices_lock);
+ list_del(&u->list);
+ mutex_unlock(&ubiblk_devices_lock);
+
+ BUG_ON(u->cache_data != NULL); /* who did not free the cache ?! */
+ kfree(u);
+ return 0;
+}
+
+static struct ubiblk_dev *ubiblk_find(int num, int vol)
+{
+ struct ubiblk_dev *pos;
+
+ mutex_lock(&ubiblk_devices_lock);
+ list_for_each_entry(pos, &ubiblk_devices, list)
+ if (pos->ubi_num == num && pos->ubi_vol == vol) {
+ mutex_unlock(&ubiblk_devices_lock);
+ return pos;
+ }
+ mutex_unlock(&ubiblk_devices_lock);
+ return NULL;
+}
+
+static int ubiblock_notification(struct notifier_block *blk,
+ unsigned long type, void *v)
+{
+ struct ubi_notification *nt = v;
+ struct ubiblk_dev *u;
+
+ switch (type) {
+ case UBI_VOLUME_ADDED:
+ ubiblk_add(nt->vi.ubi_num, nt->vi.vol_id);
+ break;
+ case UBI_VOLUME_REMOVED:
+ u = ubiblk_find(nt->vi.ubi_num, nt->vi.vol_id);
+ if (u)
+ ubiblk_del(u);
+ break;
+ case UBI_VOLUME_RENAMED:
+ case UBI_VOLUME_RESIZED:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block ubiblock_nb = {
+ .notifier_call = ubiblock_notification,
+};
+
+static int __init ubiblock_init(void)
+{
+ int r;
+
+ mutex_init(&ubiblk_devices_lock);
+ r = register_mtd_blktrans(&ubiblock_tr);
+ if (r)
+ goto out;
+ r = ubi_register_volume_notifier(&ubiblock_nb, 0);
+ if (r)
+ goto out_unreg;
+ return 0;
+
+out_unreg:
+ deregister_mtd_blktrans(&ubiblock_tr);
+out:
+ return 0;
+}
+
+static void __exit ubiblock_exit(void)
+{
+ ubi_unregister_volume_notifier(&ubiblock_nb);
+ deregister_mtd_blktrans(&ubiblock_tr);
+}
+
+module_init(ubiblock_init);
+module_exit(ubiblock_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Caching block device emulation access to UBI devices");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");