All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
@ 2011-08-24  7:33 ` Huang Shijie
  0 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-24  7:33 UTC (permalink / raw)
  To: dedekind1
  Cc: koen.beel.barco, w.sang, Huang Shijie, linux-mtd, shijie8,
	s.hauer, linux-arm-kernel

The patch set is based on Artem's tree:
	http://git.infradead.org/users/dedekind/l2-mtd-2.6.git

The general-purpose media interface(GPMI) controller is a flexible interface
to up to several NAND flashs.

The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.

With the help of BCH, the GPMI controller can choose to do the hardware ECC or
not.

This driver is a _pure_ MTD NAND controller driver now.


The driver depends on another GPMI-NAND device patch set, you can find them at :
	[1] http://marc.info/?l=linux-arm-kernel&m=131416901319573&w=2
	[2] http://marc.info/?l=linux-arm-kernel&m=131416912319668&w=2
	[3] http://marc.info/?l=linux-arm-kernel&m=131416891119504&w=2
	[4] http://marc.info/?l=linux-arm-kernel&m=131416896219539&w=2

Test environment:
	Using imx23 and imx28 boards for test. 

	imx23 :
	console=ttyAMA0,115200 mtdparts=gpmi-nfc:20m(boot),-(user)
	Tested by USB boot and NAND boot.

        imx28 :
	#console=ttyAMA0,115200 root=/dev/mmcblk0p3 rw rootwait 
	Tested by SD card boot mode.

v9 --> v10:
	[0] fix DMA timeout bug by adding custom routine for reset blocks.
	[1] remove mil{} 
	[2] add gpmi_hw_ecclayout.
	[3] add ecc_write_oob() again to prohibit the mtd ioctl : MEMWRITEOOB
	[4] change pr_info to dev_dbg() or pr_err().
	[5] merge the all the dump functions into gpmi_dump_info().
	[6] remove parse_mtd_partitions(), and use mtd_device_parse_register().
	[7] move the the debug control code from gpmi-nand.h to gpmi-nand.c
	[8] renames
	[9] others

v8 --> v9:
	[0] remove the ONFI nand code, it will cause the DMA timeout in the ONFI nand.
	[1] remove sysfs entry.
	[2] remove kernel command parameter.
	[3] report to ecclayout that we will use all the page + OOB.
		remove the ->ecc_write_oob().
	[4] add our own block_markbad() hook.
	[5] rename some functions from mil_* to gpmi_*.
	[6] replace the __raw_readl/__raw_writel to readl/writel.
	[7] others

v7 --> v8:
	[0] rename the name from `GPMI-NFC` to `GPMI-NAND`
	[1] remove the `hal` layer, and change it to function library.
	[2] Do not use ~0 to initialize the DMA address.
	[3] fix the issue : several DMA channels share the same IRQ.
	[4] DMA timeout issue. I use the .config created by `make mxs_defconfig`
	    and the bug never occur. It seems some other module has impact to the
	    DMA.
	    Of course, you should enable the UBIFS,MTD_CHAR and GPMI for
	    the .config. You can also disable the SPIN-LOCK/MUTEX debug features.
	[5] add function to print out the GPMI registers.	    
	[6] others.	    

v6 --> v7:
	[0] remove the function wrapping the clock.
	[1] use the module_param() for debugging.
	[2] use the new interface of MTD partitions.
		replace add_mtd_partitions() with mtd_device_register().
	[3] use pr_info() to print log.
	[4] add `__devinit` for some functions, etc.
	[5] add `gpmi_nfc` to control the GPMI-NFC driver's initialization.
	[6] others.

v5 --> v6:
	[0] split out the IMX23/IMX28 arch code to separate patches.
	[1] fix bug : missing empty item in the end of platform_id array.
	[2] inconsistent identation.
	[3] others

v4 --> v5:
	[0] rename the files.
	[1] fix PM bug
	[2] remove the rom_helper code, and move the necessary initialization code
		to the main file.
	[3] change the macros from CPU_IS_* to GPMI_IS_*
	[4] remove the default partition layout init code. revert back the 
		partition parsing command line code.
	[5] others

v3 --> v4:
	[0] use the nand_ids{} as the nand database, drop my own database.
	[1] remove the patch for DMA enginer, Shawn will submit his own version.
	[2] use the platform_id to distinguish different Archs.
	[3] fix the strange coding style.
	[4] others.

v2 --> v3:
	[0] merge the imx23 and imx28 into one file(including the header file).
	[1] remove the unuse registers in the headers.
	[2] fix DMA bugs
	[3] add bus width field to nand_attr{}
	[4] others

v1 --> v2:
	[0] merge the common files into the gpmi-nfc-main.c
	[1] change the code to get the clock.
	[2] remove the timing in the nand_device_info{}
	[3] fix DMA errors
	[4] add the nand_device_info.[ch] to generic code
	[5] use the chip->onfi_version for the ONFI nand
	[6] useless init
	[7] others

Huang Shijie (3):
  MTD : add the common code for GPMI-NAND controller driver
  MTD : add helper functions library and header files for GPMI NAND
    driver
  MTD : add GPMI-NAND driver in the config and Makefile

 drivers/mtd/nand/Kconfig               |   13 +
 drivers/mtd/nand/Makefile              |    1 +
 drivers/mtd/nand/gpmi-nand/Makefile    |    3 +
 drivers/mtd/nand/gpmi-nand/bch-regs.h  |   84 ++
 drivers/mtd/nand/gpmi-nand/gpmi-lib.c  | 1056 ++++++++++++++++++++
 drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 1674 ++++++++++++++++++++++++++++++++
 drivers/mtd/nand/gpmi-nand/gpmi-nand.h |  274 ++++++
 drivers/mtd/nand/gpmi-nand/gpmi-regs.h |  168 ++++
 8 files changed, 3273 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/gpmi-nand/Makefile
 create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.c
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.h
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
@ 2011-08-24  7:33 ` Huang Shijie
  0 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-24  7:33 UTC (permalink / raw)
  To: linux-arm-kernel

The patch set is based on Artem's tree:
	http://git.infradead.org/users/dedekind/l2-mtd-2.6.git

The general-purpose media interface(GPMI) controller is a flexible interface
to up to several NAND flashs.

The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.

With the help of BCH, the GPMI controller can choose to do the hardware ECC or
not.

This driver is a _pure_ MTD NAND controller driver now.


The driver depends on another GPMI-NAND device patch set, you can find them at :
	[1] http://marc.info/?l=linux-arm-kernel&m=131416901319573&w=2
	[2] http://marc.info/?l=linux-arm-kernel&m=131416912319668&w=2
	[3] http://marc.info/?l=linux-arm-kernel&m=131416891119504&w=2
	[4] http://marc.info/?l=linux-arm-kernel&m=131416896219539&w=2

Test environment:
	Using imx23 and imx28 boards for test. 

	imx23 :
	console=ttyAMA0,115200 mtdparts=gpmi-nfc:20m(boot),-(user)
	Tested by USB boot and NAND boot.

        imx28 :
	#console=ttyAMA0,115200 root=/dev/mmcblk0p3 rw rootwait 
	Tested by SD card boot mode.

v9 --> v10:
	[0] fix DMA timeout bug by adding custom routine for reset blocks.
	[1] remove mil{} 
	[2] add gpmi_hw_ecclayout.
	[3] add ecc_write_oob() again to prohibit the mtd ioctl : MEMWRITEOOB
	[4] change pr_info to dev_dbg() or pr_err().
	[5] merge the all the dump functions into gpmi_dump_info().
	[6] remove parse_mtd_partitions(), and use mtd_device_parse_register().
	[7] move the the debug control code from gpmi-nand.h to gpmi-nand.c
	[8] renames
	[9] others

v8 --> v9:
	[0] remove the ONFI nand code, it will cause the DMA timeout in the ONFI nand.
	[1] remove sysfs entry.
	[2] remove kernel command parameter.
	[3] report to ecclayout that we will use all the page + OOB.
		remove the ->ecc_write_oob().
	[4] add our own block_markbad() hook.
	[5] rename some functions from mil_* to gpmi_*.
	[6] replace the __raw_readl/__raw_writel to readl/writel.
	[7] others

v7 --> v8:
	[0] rename the name from `GPMI-NFC` to `GPMI-NAND`
	[1] remove the `hal` layer, and change it to function library.
	[2] Do not use ~0 to initialize the DMA address.
	[3] fix the issue : several DMA channels share the same IRQ.
	[4] DMA timeout issue. I use the .config created by `make mxs_defconfig`
	    and the bug never occur. It seems some other module has impact to the
	    DMA.
	    Of course, you should enable the UBIFS,MTD_CHAR and GPMI for
	    the .config. You can also disable the SPIN-LOCK/MUTEX debug features.
	[5] add function to print out the GPMI registers.	    
	[6] others.	    

v6 --> v7:
	[0] remove the function wrapping the clock.
	[1] use the module_param() for debugging.
	[2] use the new interface of MTD partitions.
		replace add_mtd_partitions() with mtd_device_register().
	[3] use pr_info() to print log.
	[4] add `__devinit` for some functions, etc.
	[5] add `gpmi_nfc` to control the GPMI-NFC driver's initialization.
	[6] others.

v5 --> v6:
	[0] split out the IMX23/IMX28 arch code to separate patches.
	[1] fix bug : missing empty item in the end of platform_id array.
	[2] inconsistent identation.
	[3] others

v4 --> v5:
	[0] rename the files.
	[1] fix PM bug
	[2] remove the rom_helper code, and move the necessary initialization code
		to the main file.
	[3] change the macros from CPU_IS_* to GPMI_IS_*
	[4] remove the default partition layout init code. revert back the 
		partition parsing command line code.
	[5] others

v3 --> v4:
	[0] use the nand_ids{} as the nand database, drop my own database.
	[1] remove the patch for DMA enginer, Shawn will submit his own version.
	[2] use the platform_id to distinguish different Archs.
	[3] fix the strange coding style.
	[4] others.

v2 --> v3:
	[0] merge the imx23 and imx28 into one file(including the header file).
	[1] remove the unuse registers in the headers.
	[2] fix DMA bugs
	[3] add bus width field to nand_attr{}
	[4] others

v1 --> v2:
	[0] merge the common files into the gpmi-nfc-main.c
	[1] change the code to get the clock.
	[2] remove the timing in the nand_device_info{}
	[3] fix DMA errors
	[4] add the nand_device_info.[ch] to generic code
	[5] use the chip->onfi_version for the ONFI nand
	[6] useless init
	[7] others

Huang Shijie (3):
  MTD : add the common code for GPMI-NAND controller driver
  MTD : add helper functions library and header files for GPMI NAND
    driver
  MTD : add GPMI-NAND driver in the config and Makefile

 drivers/mtd/nand/Kconfig               |   13 +
 drivers/mtd/nand/Makefile              |    1 +
 drivers/mtd/nand/gpmi-nand/Makefile    |    3 +
 drivers/mtd/nand/gpmi-nand/bch-regs.h  |   84 ++
 drivers/mtd/nand/gpmi-nand/gpmi-lib.c  | 1056 ++++++++++++++++++++
 drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 1674 ++++++++++++++++++++++++++++++++
 drivers/mtd/nand/gpmi-nand/gpmi-nand.h |  274 ++++++
 drivers/mtd/nand/gpmi-nand/gpmi-regs.h |  168 ++++
 8 files changed, 3273 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/gpmi-nand/Makefile
 create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.c
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.h
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 1/3] MTD : add the common code for GPMI-NAND controller driver
  2011-08-24  7:33 ` Huang Shijie
@ 2011-08-24  7:33   ` Huang Shijie
  -1 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-24  7:33 UTC (permalink / raw)
  To: dedekind1
  Cc: koen.beel.barco, w.sang, Huang Shijie, linux-mtd, shijie8,
	s.hauer, linux-arm-kernel

These files contain the common code for the GPMI-NAND driver.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 1674 ++++++++++++++++++++++++++++++++
 drivers/mtd/nand/gpmi-nand/gpmi-nand.h |  274 ++++++
 2 files changed, 1948 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.c
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.h

diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
new file mode 100644
index 0000000..99940bd
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -0,0 +1,1674 @@
+/*
+ * Freescale GPMI 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/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/gpmi-nand.h>
+#include <linux/mtd/partitions.h>
+
+#include "gpmi-nand.h"
+
+/* 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
+};
+
+/*  We will use all the (page + OOB). */
+static struct nand_ecclayout gpmi_hw_ecclayout = {
+	.eccbytes = 0,
+	.eccpos = { 0, },
+	.oobfree = { {.offset = 0, .length = 0} }
+};
+
+/* debug control */
+#define GPMI_DEBUG_READ_OOB	0x0001
+#define GPMI_DEBUG_READ		0x0002
+#define GPMI_DEBUG_WRITE	0x0004
+#define GPMI_DEBUG_ECC_READ	0x0008
+#define GPMI_DEBUG_ECC_WRITE	0x0010
+#define GPMI_DEBUG_VERBOSE	0x0020
+
+#define logio(level)				\
+		do {				\
+			if (gpmi_debug & level)	\
+				pr_info("%s : %d\n", __func__, __LINE__); \
+		} while (0)
+
+int gpmi_debug;
+module_param(gpmi_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(gpmi_debug, "print out the debug info.");
+
+static irqreturn_t bch_irq(int irq, void *cookie)
+{
+	struct gpmi_nand_data *this = cookie;
+
+	gpmi_clear_bch(this);
+	complete(&this->bch_done);
+	return IRQ_HANDLED;
+}
+
+/* calculate the ECC strength by hand */
+static inline int get_ecc_strength(struct gpmi_nand_data *this)
+{
+	struct mtd_info	*mtd = &this->mtd;
+	int ecc_strength = 0;
+
+	switch (mtd->writesize) {
+	case 2048:
+		ecc_strength = 8;
+		break;
+	case 4096:
+		switch (mtd->oobsize) {
+		case 128:
+			ecc_strength = 8;
+			break;
+		case 224:
+		case 218:
+			ecc_strength = 16;
+			break;
+		}
+		break;
+	case 8192:
+		ecc_strength = 24;
+		break;
+	}
+
+	return ecc_strength;
+}
+
+int common_nfc_set_geometry(struct gpmi_nand_data *this)
+{
+	struct bch_geometry *geo = &this->bch_geometry;
+	struct mtd_info *mtd = &this->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;
+	int gf_len = 13;/* use GP13 by default */
+
+	/* We only support BCH now. */
+	geo->ecc_algorithm = "BCH";
+
+	/*
+	 * The size of the metadata can be changed, though we set it to 10
+	 * bytes now. But it can't be too large, because we have to save
+	 * enough space for BCH.
+	 */
+	geo->metadata_size_in_bytes = 10;
+
+	/* ECC chunks, used by default. */
+	geo->ecc_chunk_size_in_bytes = 512;
+
+	/*
+	 * 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.
+	 */
+	geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size_in_bytes;
+
+	/*
+	 * We use the same ECC strength for all chunks, including the first one.
+	 */
+	geo->ecc_strength = get_ecc_strength(this);
+	if (!geo->ecc_strength) {
+		pr_err("Page size:%d, OOB:%d\n", mtd->writesize, mtd->oobsize);
+		return -EINVAL;
+	}
+
+	/* Compute the page size, include page and oob. */
+	geo->page_size_in_bytes = mtd->writesize + mtd->oobsize;
+	geo->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 = ALIGN(geo->metadata_size_in_bytes, 4);
+	status_size   = ALIGN(geo->ecc_chunk_count, 4);
+
+	geo->auxiliary_size_in_bytes = metadata_size + status_size;
+	geo->auxiliary_status_offset = metadata_size;
+
+	/* Check if we're going to do block mark swapping. */
+	if (!this->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 = geo->ecc_chunk_size_in_bytes * 8;
+	chunk_ecc_size_in_bits  = geo->ecc_strength * gf_len;
+	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 -= geo->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.
+		 */
+		pr_err("Unsupported page geometry : %u:%u\n",
+				mtd->writesize, mtd->oobsize);
+		return -EINVAL;
+	}
+
+	/*
+	 * 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.
+	 */
+	geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+	geo->block_mark_bit_offset  = block_mark_bit_offset % 8;
+
+	return 0;
+}
+
+struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
+{
+	int chipnr = this->current_chip;
+
+	BUG_ON(chipnr < 0);
+	return this->dma_chans[chipnr];
+}
+
+/* Can we use the upper's buffer directly for DMA? */
+void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr)
+{
+	struct scatterlist *sgl = &this->data_sgl;
+	int ret;
+
+	this->direct_dma_map_ok = true;
+
+	/* first try to map the upper buffer directly */
+	sg_init_one(sgl, this->upper_buf, this->upper_len);
+	ret = dma_map_sg(this->dev, sgl, 1, dr);
+	if (ret == 0) {
+		/* We have to use our own DMA buffer. */
+		sg_init_one(sgl, this->data_buffer_dma, PAGE_SIZE);
+
+		if (dr == DMA_TO_DEVICE)
+			memcpy(this->data_buffer_dma, this->upper_buf,
+				this->upper_len);
+
+		ret = dma_map_sg(this->dev, sgl, 1, dr);
+		BUG_ON(ret == 0);
+
+		this->direct_dma_map_ok = false;
+	}
+}
+
+/* This will be called after the DMA operation is finished. */
+static void dma_irq_callback(void *param)
+{
+	struct gpmi_nand_data *this = param;
+	struct completion *dma_c = &this->dma_done;
+
+	complete(dma_c);
+
+	switch (this->dma_type) {
+	case DMA_FOR_COMMAND:
+		dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE);
+		break;
+
+	case DMA_FOR_READ_DATA:
+		dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE);
+		if (this->direct_dma_map_ok == false)
+			memcpy(this->upper_buf, this->data_buffer_dma,
+				this->upper_len);
+		break;
+
+	case DMA_FOR_WRITE_DATA:
+		dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE);
+		break;
+
+	case DMA_FOR_READ_ECC_PAGE:
+	case DMA_FOR_WRITE_ECC_PAGE:
+		/* We have to wait the BCH interrupt to finish. */
+		break;
+
+	default:
+		BUG();
+	}
+}
+
+int start_dma_without_bch_irq(struct gpmi_nand_data *this,
+				struct dma_async_tx_descriptor *desc)
+{
+	struct completion *dma_c = &this->dma_done;
+	int err;
+
+	init_completion(dma_c);
+
+	desc->callback		= dma_irq_callback;
+	desc->callback_param	= this;
+	dmaengine_submit(desc);
+
+	/* Wait for the interrupt from the DMA block. */
+	err = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
+	err = (!err) ? -ETIMEDOUT : 0;
+	if (err) {
+		pr_err("DMA timeout, last DMA :%d\n", this->last_dma_type);
+		if (gpmi_debug & GPMI_DEBUG_VERBOSE)
+			gpmi_dump_info(this);
+	}
+	return err;
+}
+
+/*
+ * This function is used in BCH reading or BCH writing pages.
+ * It will wait for the BCH interrupt as long as ONE second.
+ * Actually, we must wait for two interrupts :
+ *	[1] firstly the DMA interrupt and
+ *	[2] secondly the BCH interrupt.
+ */
+int start_dma_with_bch_irq(struct gpmi_nand_data *this,
+			struct dma_async_tx_descriptor *desc)
+{
+	int err;
+
+	/* Prepare to receive an interrupt from the BCH block. */
+	init_completion(&this->bch_done);
+
+	/* start the DMA */
+	start_dma_without_bch_irq(this, desc);
+
+	/* Wait for the interrupt from the BCH block. */
+	err = wait_for_completion_timeout(&this->bch_done,
+					msecs_to_jiffies(1000));
+	err = (!err) ? -ETIMEDOUT : 0;
+	if (err) {
+		pr_err("BCH timeout!\n");
+		if (gpmi_debug & GPMI_DEBUG_VERBOSE)
+			gpmi_dump_info(this);
+	}
+	return err;
+}
+
+static int __devinit
+acquire_register_block(struct gpmi_nand_data *this, const char *res_name)
+{
+	struct platform_device *pdev = this->pdev;
+	struct resources *res = &this->resources;
+	struct resource *r;
+	void *p;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
+	if (!r) {
+		pr_err("Can't get resource for %s\n", res_name);
+		return -ENXIO;
+	}
+
+	p = ioremap(r->start, resource_size(r));
+	if (!p) {
+		pr_err("Can't remap %s\n", res_name);
+		return -ENOMEM;
+	}
+
+	if (res_name == GPMI_NAND_GPMI_REGS_ADDR_RES_NAME)
+		res->gpmi_regs = p;
+	else if (res_name == GPMI_NAND_BCH_REGS_ADDR_RES_NAME)
+		res->bch_regs = p;
+	else
+		BUG();
+
+	return 0;
+}
+
+static void release_register_block(struct gpmi_nand_data *this)
+{
+	struct resources *res = &this->resources;
+	if (res->gpmi_regs)
+		iounmap(res->gpmi_regs);
+	if (res->bch_regs)
+		iounmap(res->bch_regs);
+	res->gpmi_regs = NULL;
+	res->bch_regs = NULL;
+}
+
+static int __devinit
+acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
+{
+	struct platform_device *pdev = this->pdev;
+	struct resources *res = &this->resources;
+	const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME;
+	struct resource *r;
+	int err;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
+	if (!r) {
+		pr_err("Can't get resource for %s\n", res_name);
+		return -ENXIO;
+	}
+
+	BUG_ON(r->start != r->end);
+	err = request_irq(r->start, irq_h, 0, res_name, this);
+	if (err) {
+		pr_err("Can't own %s\n", res_name);
+		return err;
+	}
+
+	res->bch_low_interrupt = r->start;
+	res->bch_high_interrupt = r->end;
+	return 0;
+}
+
+static void release_bch_irq(struct gpmi_nand_data *this)
+{
+	struct resources *res = &this->resources;
+	int i = res->bch_low_interrupt;
+
+	for (; i <= res->bch_high_interrupt; i++)
+		free_irq(i, this);
+}
+
+static bool gpmi_dma_filter(struct dma_chan *chan, void *param)
+{
+	struct gpmi_nand_data *this = param;
+	struct resource *r = this->private;
+
+	if (!mxs_dma_is_apbh(chan))
+		return false;
+	/*
+	 * only catch the GPMI dma channels :
+	 *	for mx23 :	MX23_DMA_GPMI0 ~ MX23_DMA_GPMI3
+	 *		(These four channels share the same IRQ!)
+	 *
+	 *	for mx28 :	MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7
+	 *		(These eight channels share the same IRQ!)
+	 */
+	if (r->start <= chan->chan_id && chan->chan_id <= r->end) {
+		chan->private = &this->dma_data;
+		return true;
+	}
+	return false;
+}
+
+static void release_dma_channels(struct gpmi_nand_data *this)
+{
+	unsigned int i;
+	for (i = 0; i < DMA_CHANS; i++)
+		if (this->dma_chans[i]) {
+			dma_release_channel(this->dma_chans[i]);
+			this->dma_chans[i] = NULL;
+		}
+}
+
+static int __devinit acquire_dma_channels(struct gpmi_nand_data *this)
+{
+	struct platform_device *pdev = this->pdev;
+	struct gpmi_nand_platform_data *pdata = this->pdata;
+	struct resources *res = &this->resources;
+	struct resource *r, *r_dma;
+	unsigned int i;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+					GPMI_NAND_DMA_CHANNELS_RES_NAME);
+	r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					GPMI_NAND_DMA_INTERRUPT_RES_NAME);
+	if (!r || !r_dma) {
+		pr_err("Can't get resource for DMA\n");
+		return -ENXIO;
+	}
+
+	/* used in gpmi_dma_filter() */
+	this->private = r;
+
+	for (i = r->start; i <= r->end; i++) {
+		struct dma_chan *dma_chan;
+		dma_cap_mask_t mask;
+
+		if (i - r->start >= pdata->max_chip_count)
+			break;
+
+		dma_cap_zero(mask);
+		dma_cap_set(DMA_SLAVE, mask);
+
+		/* get the DMA interrupt */
+		if (r_dma->start == r_dma->end) {
+			/* only register the first. */
+			if (i == r->start)
+				this->dma_data.chan_irq = r_dma->start;
+			else
+				this->dma_data.chan_irq = NO_IRQ;
+		} else
+			this->dma_data.chan_irq = r_dma->start + (i - r->start);
+
+		dma_chan = dma_request_channel(mask, gpmi_dma_filter, this);
+		if (!dma_chan)
+			goto acquire_err;
+
+		/* fill the first empty item */
+		this->dma_chans[i - r->start] = dma_chan;
+	}
+
+	res->dma_low_channel = r->start;
+	res->dma_high_channel = i;
+	return 0;
+
+acquire_err:
+	pr_err("Can't acquire DMA channel %u\n", i);
+	release_dma_channels(this);
+	return -EINVAL;
+}
+
+static int __devinit acquire_resources(struct gpmi_nand_data *this)
+{
+	struct resources *res = &this->resources;
+	int ret;
+
+	ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME);
+	if (ret)
+		goto exit_regs;
+
+	ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME);
+	if (ret)
+		goto exit_regs;
+
+	ret = acquire_bch_irq(this, bch_irq);
+	if (ret)
+		goto exit_regs;
+
+	ret = acquire_dma_channels(this);
+	if (ret)
+		goto exit_dma_channels;
+
+	res->clock = clk_get(&this->pdev->dev, NULL);
+	if (IS_ERR(res->clock)) {
+		pr_err("can not get the clock\n");
+		ret = -ENOENT;
+		goto exit_clock;
+	}
+	return 0;
+
+exit_clock:
+	release_dma_channels(this);
+exit_dma_channels:
+	release_bch_irq(this);
+exit_regs:
+	release_register_block(this);
+	return ret;
+}
+
+static void release_resources(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+
+	clk_put(r->clock);
+	release_register_block(this);
+	release_bch_irq(this);
+	release_dma_channels(this);
+}
+
+static int __devinit init_hardware(struct gpmi_nand_data *this)
+{
+	int ret;
+
+	/*
+	 * This structure contains the "safe" GPMI timing that should succeed
+	 * with any NAND Flash device
+	 * (although, with less-than-optimal performance).
+	 */
+	struct nand_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,
+	};
+
+	/* Initialize the hardwares. */
+	ret = gpmi_init(this);
+	if (ret)
+		return ret;
+
+	this->timing = safe_timing;
+	return 0;
+}
+
+static int read_page_prepare(struct gpmi_nand_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;
+
+	if (virt_addr_valid(destination)) {
+		dma_addr_t dest_phys;
+
+		dest_phys = dma_map_single(dev, destination,
+						length, DMA_FROM_DEVICE);
+		if (dma_mapping_error(dev, dest_phys)) {
+			if (alt_size < length) {
+				pr_err("Alternate buffer is too small\n");
+				return -ENOMEM;
+			}
+			goto map_failed;
+		}
+		*use_virt = destination;
+		*use_phys = dest_phys;
+		this->direct_dma_map_ok = true;
+		return 0;
+	}
+
+map_failed:
+	*use_virt = alt_virt;
+	*use_phys = alt_phys;
+	this->direct_dma_map_ok = false;
+	return 0;
+}
+
+static inline void read_page_end(struct gpmi_nand_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)
+{
+	if (this->direct_dma_map_ok)
+		dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE);
+}
+
+static inline void read_page_swap_end(struct gpmi_nand_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)
+{
+	if (!this->direct_dma_map_ok)
+		memcpy(destination, alt_virt, length);
+}
+
+static int send_page_prepare(struct gpmi_nand_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;
+
+	if (virt_addr_valid(source)) {
+		dma_addr_t source_phys;
+
+		source_phys = dma_map_single(dev, (void *)source, length,
+						DMA_TO_DEVICE);
+		if (dma_mapping_error(dev, source_phys)) {
+			if (alt_size < length) {
+				pr_err("Alternate buffer is too small\n");
+				return -ENOMEM;
+			}
+			goto map_failed;
+		}
+		*use_virt = source;
+		*use_phys = source_phys;
+		return 0;
+	}
+map_failed:
+	/*
+	 * Copy the content 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;
+	return 0;
+}
+
+static void send_page_end(struct gpmi_nand_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;
+	if (used_virt == source)
+		dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE);
+}
+
+static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
+{
+	struct device *dev = this->dev;
+
+	if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt))
+		dma_free_coherent(dev, this->page_buffer_size,
+					this->page_buffer_virt,
+					this->page_buffer_phys);
+	kfree(this->cmd_buffer);
+	kfree(this->data_buffer_dma);
+
+	this->cmd_buffer		= NULL;
+	this->data_buffer_dma	= NULL;
+	this->page_buffer_virt	= NULL;
+	this->page_buffer_size	=  0;
+}
+
+/* Allocate the DMA buffers */
+static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
+{
+	struct bch_geometry *geo = &this->bch_geometry;
+	struct device *dev = this->dev;
+
+	/* [1] Allocate a command buffer. PAGE_SIZE is enough. */
+	this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA);
+	if (this->cmd_buffer == NULL)
+		goto error_alloc;
+
+	/* [2] Allocate a read/write data buffer. PAGE_SIZE is enough. */
+	this->data_buffer_dma = kzalloc(PAGE_SIZE, GFP_DMA);
+	if (this->data_buffer_dma == NULL)
+		goto error_alloc;
+
+	/*
+	 * [3] 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.
+	 */
+	this->page_buffer_size = geo->payload_size_in_bytes +
+				geo->auxiliary_size_in_bytes;
+
+	this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size,
+					&this->page_buffer_phys, GFP_DMA);
+	if (!this->page_buffer_virt)
+		goto error_alloc;
+
+
+	/* Slice up the page buffer. */
+	this->payload_virt = this->page_buffer_virt;
+	this->payload_phys = this->page_buffer_phys;
+	this->auxiliary_virt = this->payload_virt + geo->payload_size_in_bytes;
+	this->auxiliary_phys = this->payload_phys + geo->payload_size_in_bytes;
+	return 0;
+
+error_alloc:
+	gpmi_free_dma_buffer(this);
+	pr_err("allocate DMA buffer ret!!\n");
+	return -ENOMEM;
+}
+
+static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+	int ret;
+
+	/*
+	 * 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. NAND_CMD_NONE means the END of the queue.
+	 */
+	if ((ctrl & (NAND_ALE | NAND_CLE))) {
+		if (data != NAND_CMD_NONE)
+			this->cmd_buffer[this->command_length++] = data;
+		return;
+	}
+
+	if (!this->command_length)
+		return;
+
+	ret = gpmi_send_command(this);
+	if (ret)
+		pr_err("Chip: %u, Error %d\n", this->current_chip, ret);
+
+	this->command_length = 0;
+}
+
+static int gpmi_dev_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+
+	return gpmi_is_ready(this, this->current_chip);
+}
+
+static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+
+	if ((this->current_chip < 0) && (chipnr >= 0))
+		gpmi_begin(this);
+	else if ((this->current_chip >= 0) && (chipnr < 0))
+		gpmi_end(this);
+
+	this->current_chip = chipnr;
+}
+
+static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+
+	logio(GPMI_DEBUG_READ);
+	this->upper_buf	= buf;
+	this->upper_len	= len;
+
+	gpmi_read_data(this);
+}
+
+static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+
+	logio(GPMI_DEBUG_WRITE);
+	this->upper_buf	= (uint8_t *)buf;
+	this->upper_len	= len;
+
+	gpmi_send_data(this);
+}
+
+static uint8_t gpmi_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+	uint8_t *buf = this->data_buffer_dma;
+
+	gpmi_read_buf(mtd, buf, 1);
+	return buf[0];
+}
+
+/*
+ * Handles block mark swapping.
+ * It can be called in swapping the block mark, or swapping it back,
+ * because the the operations are the same.
+ */
+static void block_mark_swapping(struct gpmi_nand_data *this,
+				void *payload, void *auxiliary)
+{
+	struct bch_geometry *nfc_geo = &this->bch_geometry;
+	unsigned char *p;
+	unsigned char *a;
+	unsigned int  bit;
+	unsigned char mask;
+	unsigned char from_data;
+	unsigned char from_oob;
+
+	if (!this->swap_block_mark)
+		return;
+
+	/*
+	 * If control arrives here, we're swapping. Make some convenience
+	 * variables.
+	 */
+	bit = nfc_geo->block_mark_bit_offset;
+	p   = 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));
+}
+
+static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf, int page)
+{
+	struct gpmi_nand_data *this = chip->priv;
+	struct bch_geometry *nfc_geo = &this->bch_geometry;
+	void          *payload_virt;
+	dma_addr_t    payload_phys;
+	void          *auxiliary_virt;
+	dma_addr_t    auxiliary_phys;
+	unsigned int  i;
+	unsigned char *status;
+	unsigned int  failed;
+	unsigned int  corrected;
+	int           ret;
+
+	logio(GPMI_DEBUG_ECC_READ);
+	ret = read_page_prepare(this, buf, mtd->writesize,
+					this->payload_virt, this->payload_phys,
+					nfc_geo->payload_size_in_bytes,
+					&payload_virt, &payload_phys);
+	if (ret) {
+		pr_err("Inadequate DMA buffer\n");
+		ret = -ENOMEM;
+		return ret;
+	}
+	auxiliary_virt = this->auxiliary_virt;
+	auxiliary_phys = this->auxiliary_phys;
+
+	/* go! */
+	ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
+	read_page_end(this, buf, mtd->writesize,
+			this->payload_virt, this->payload_phys,
+			nfc_geo->payload_size_in_bytes,
+			payload_virt, payload_phys);
+	if (ret) {
+		pr_err("Error in ECC-based read: %d\n", ret);
+		goto exit_nfc;
+	}
+
+	/* handle the block mark swapping */
+	block_mark_swapping(this, payload_virt, auxiliary_virt);
+
+	/* Loop over status bytes, accumulating ECC status. */
+	failed		= 0;
+	corrected	= 0;
+	status		= auxiliary_virt + nfc_geo->auxiliary_status_offset;
+
+	for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
+		if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
+			continue;
+
+		if (*status == STATUS_UNCORRECTABLE) {
+			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 gpmi_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(chip->oob_poi, ~0, mtd->oobsize);
+	chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
+
+	read_page_swap_end(this, buf, mtd->writesize,
+			this->payload_virt, this->payload_phys,
+			nfc_geo->payload_size_in_bytes,
+			payload_virt, payload_phys);
+exit_nfc:
+	return ret;
+}
+
+static void gpmi_ecc_write_page(struct mtd_info *mtd,
+				struct nand_chip *chip, const uint8_t *buf)
+{
+	struct gpmi_nand_data *this = chip->priv;
+	struct bch_geometry *nfc_geo = &this->bch_geometry;
+	const void *payload_virt;
+	dma_addr_t payload_phys;
+	const void *auxiliary_virt;
+	dma_addr_t auxiliary_phys;
+	int        ret;
+
+	logio(GPMI_DEBUG_ECC_WRITE);
+	if (this->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(this->payload_virt, buf, mtd->writesize);
+		payload_virt = this->payload_virt;
+		payload_phys = this->payload_phys;
+
+		memcpy(this->auxiliary_virt, chip->oob_poi,
+				nfc_geo->auxiliary_size_in_bytes);
+		auxiliary_virt = this->auxiliary_virt;
+		auxiliary_phys = this->auxiliary_phys;
+
+		/* Handle block mark swapping. */
+		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.
+		 */
+		ret = send_page_prepare(this,
+				buf, mtd->writesize,
+				this->payload_virt, this->payload_phys,
+				nfc_geo->payload_size_in_bytes,
+				&payload_virt, &payload_phys);
+		if (ret) {
+			pr_err("Inadequate payload DMA buffer\n");
+			return;
+		}
+
+		ret = send_page_prepare(this,
+				chip->oob_poi, mtd->oobsize,
+				this->auxiliary_virt, this->auxiliary_phys,
+				nfc_geo->auxiliary_size_in_bytes,
+				&auxiliary_virt, &auxiliary_phys);
+		if (ret) {
+			pr_err("Inadequate auxiliary DMA buffer\n");
+			goto exit_auxiliary;
+		}
+	}
+
+	/* Ask the NFC. */
+	ret = gpmi_send_page(this, payload_phys, auxiliary_phys);
+	if (ret)
+		pr_err("Error in ECC-based write: %d\n", ret);
+
+	if (!this->swap_block_mark) {
+		send_page_end(this, chip->oob_poi, mtd->oobsize,
+				this->auxiliary_virt, this->auxiliary_phys,
+				nfc_geo->auxiliary_size_in_bytes,
+				auxiliary_virt, auxiliary_phys);
+exit_auxiliary:
+		send_page_end(this, buf, mtd->writesize,
+				this->payload_virt, this->payload_phys,
+				nfc_geo->payload_size_in_bytes,
+				payload_virt, payload_phys);
+	}
+}
+
+/*
+ * 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.
+ *
+ * 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.
+ */
+static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+				int page, int sndcmd)
+{
+	struct gpmi_nand_data *this = chip->priv;
+
+	logio(GPMI_DEBUG_READ_OOB);
+	/* clear the OOB buffer */
+	memset(chip->oob_poi, ~0, mtd->oobsize);
+
+	/* Read out the conventional OOB. */
+	chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+	chip->read_buf(mtd, chip->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 (!this->swap_block_mark) {
+		/* Read the block mark into the first byte of the OOB buffer. */
+		chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+		chip->oob_poi[0] = chip->read_byte(mtd);
+	}
+
+	/*
+	 * Return true, indicating that the next call to this function must send
+	 * a command.
+	 */
+	return true;
+}
+
+static int
+gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+	/*
+	 * The BCH will use all the (page + oob).
+	 * Our gpmi_hw_ecclayout can only prohibit the JFFS2 to write the oob.
+	 * But it can not stop some ioctls such MEMWRITEOOB which uses
+	 * MTD_OOB_PLACE. So We have to implement this function to prohibit
+	 * these ioctls too.
+	 */
+	return -EPERM;
+}
+
+static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+	int block, ret = 0;
+	uint8_t *block_mark;
+	int column, page, status, chipnr;
+
+	/* Get block number */
+	block = (int)(ofs >> chip->bbt_erase_shift);
+	if (chip->bbt)
+		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+	/* Do we have a flash based bad block table ? */
+	if (chip->options & NAND_BBT_USE_FLASH)
+		ret = nand_update_bbt(mtd, ofs);
+	else {
+		chipnr = (int)(ofs >> chip->chip_shift);
+		chip->select_chip(mtd, chipnr);
+
+		column = this->swap_block_mark ? mtd->writesize : 0;
+
+		/* Write the block mark. */
+		block_mark = this->data_buffer_dma;
+		block_mark[0] = 0; /* bad block marker */
+
+		/* Shift to get page */
+		page = (int)(ofs >> chip->page_shift);
+
+		chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page);
+		chip->write_buf(mtd, block_mark, 1);
+		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+		status = chip->waitfunc(mtd, chip);
+		if (status & NAND_STATUS_FAIL)
+			ret = -EIO;
+
+		chip->select_chip(mtd, -1);
+	}
+	if (!ret)
+		mtd->ecc_stats.badblocks++;
+
+	return ret;
+}
+
+static int __devinit nand_boot_set_geometry(struct gpmi_nand_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 0;
+}
+
+static const char  *fingerprint = "STMP";
+static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
+{
+	struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+	struct device *dev = this->dev;
+	struct mtd_info *mtd = &this->mtd;
+	struct nand_chip *chip = &this->nand;
+	unsigned int search_area_size_in_strides;
+	unsigned int stride;
+	unsigned int page;
+	loff_t byte;
+	uint8_t *buffer = chip->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;
+
+	saved_chip_number = this->current_chip;
+	chip->select_chip(mtd, 0);
+
+	/*
+	 * Loop through the first search area, looking for the NCB fingerprint.
+	 */
+	dev_dbg(dev, "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;
+
+		dev_dbg(dev, "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.
+		 */
+		chip->cmdfunc(mtd, NAND_CMD_READ0, 12, page);
+		chip->read_buf(mtd, buffer, strlen(fingerprint));
+
+		/* Look for the fingerprint. */
+		if (!memcmp(buffer, fingerprint, strlen(fingerprint))) {
+			found_an_ncb_fingerprint = true;
+			break;
+		}
+
+	}
+
+	chip->select_chip(mtd, saved_chip_number);
+
+	if (found_an_ncb_fingerprint)
+		dev_dbg(dev, "\tFound a fingerprint\n");
+	else
+		dev_dbg(dev, "\tNo fingerprint found\n");
+	return found_an_ncb_fingerprint;
+}
+
+/* Writes a transcription stamp. */
+static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
+{
+	struct device *dev = this->dev;
+	struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+	struct mtd_info *mtd = &this->mtd;
+	struct nand_chip *chip = &this->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 = chip->buffers->databuf;
+	int saved_chip_number;
+	int status;
+
+	/* Compute the search area geometry. */
+	block_size_in_pages = mtd->erasesize / mtd->writesize;
+	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;
+
+	dev_dbg(dev, "Search Area Geometry :\n");
+	dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks);
+	dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides);
+	dev_dbg(dev, "\tin Pages  : %u\n", search_area_size_in_pages);
+
+	/* Select chip 0. */
+	saved_chip_number = this->current_chip;
+	chip->select_chip(mtd, 0);
+
+	/* Loop over blocks in the first search area, erasing them. */
+	dev_dbg(dev, "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. */
+		dev_dbg(dev, "\tErasing block 0x%x\n", block);
+		chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+		chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+
+		/* Wait for the erase to finish. */
+		status = chip->waitfunc(mtd, chip);
+		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(chip->oob_poi, ~0, mtd->oobsize);
+	memcpy(buffer + 12, fingerprint, strlen(fingerprint));
+
+	/* Loop through the first search area, writing NCB fingerprints. */
+	dev_dbg(dev, "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. */
+		dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
+		chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+		chip->ecc.write_page_raw(mtd, chip, buffer);
+		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+		/* Wait for the write to finish. */
+		status = chip->waitfunc(mtd, chip);
+		if (status & NAND_STATUS_FAIL)
+			dev_err(dev, "[%s] Write failed.\n", __func__);
+	}
+
+	/* Deselect chip 0. */
+	chip->select_chip(mtd, saved_chip_number);
+	return 0;
+}
+
+static int __devinit mx23_boot_init(struct gpmi_nand_data  *this)
+{
+	struct device *dev = this->dev;
+	struct nand_chip *chip = &this->nand;
+	struct mtd_info *mtd = &this->mtd;
+	unsigned int block_count;
+	unsigned int block;
+	int     chipnr;
+	int     page;
+	loff_t  byte;
+	uint8_t block_mark;
+	int     ret = 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 (mx23_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.
+	 */
+	dev_dbg(dev, "Transcribing bad block marks...\n");
+
+	/* Compute the number of blocks in the entire medium. */
+	block_count = chip->chipsize >> chip->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.
+		 */
+		chipnr = block >> (chip->chip_shift - chip->phys_erase_shift);
+		page = block << (chip->phys_erase_shift - chip->page_shift);
+		byte = block <<  chip->phys_erase_shift;
+
+		/* Send the command to read the conventional block mark. */
+		chip->select_chip(mtd, chipnr);
+		chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+		block_mark = chip->read_byte(mtd);
+		chip->select_chip(mtd, -1);
+
+		/*
+		 * 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.
+		 */
+		if (block_mark != 0xff) {
+			dev_dbg(dev, "Transcribing mark in block %u\n", block);
+			ret = chip->block_markbad(mtd, byte);
+			if (ret)
+				dev_err(dev, "Failed to mark block bad with "
+							"ret %d\n", ret);
+		}
+	}
+
+	/* Write the stamp that indicates we've transcribed the block marks. */
+	mx23_write_transcription_stamp(this);
+	return 0;
+}
+
+static int __devinit nand_boot_init(struct gpmi_nand_data  *this)
+{
+	nand_boot_set_geometry(this);
+
+	/* This is ROM arch-specific initilization before the BBT scanning. */
+	if (GPMI_IS_MX23(this))
+		return mx23_boot_init(this);
+	return 0;
+}
+
+static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this)
+{
+	int ret;
+
+	/* Free the temporary DMA memory for reading ID. */
+	gpmi_free_dma_buffer(this);
+
+	/* Set up the NFC geometry which is used by BCH. */
+	ret = bch_set_geometry(this);
+	if (ret) {
+		pr_err("set geometry ret : %d\n", ret);
+		return ret;
+	}
+
+	/* Alloc the new DMA buffers according to the pagesize and oobsize */
+	return gpmi_alloc_dma_buffer(this);
+}
+
+static int gpmi_pre_bbt_scan(struct gpmi_nand_data  *this)
+{
+	int ret;
+
+	/* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
+	if (GPMI_IS_MX23(this))
+		this->swap_block_mark = false;
+	else
+		this->swap_block_mark = true;
+
+	/* Set up the medium geometry */
+	ret = gpmi_set_geometry(this);
+	if (ret)
+		return ret;
+
+	/* NAND boot init, depends on the gpmi_set_geometry(). */
+	return nand_boot_init(this);
+}
+
+static int gpmi_scan_bbt(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+	int ret;
+
+	/* Prepare for the BBT scan. */
+	ret = gpmi_pre_bbt_scan(this);
+	if (ret)
+		return ret;
+
+	/* use the default BBT implementation */
+	return nand_default_bbt(mtd);
+}
+
+void gpmi_nfc_exit(struct gpmi_nand_data *this)
+{
+	nand_release(&this->mtd);
+	gpmi_free_dma_buffer(this);
+}
+
+static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
+{
+	struct gpmi_nand_platform_data *pdata = this->pdata;
+	struct mtd_info  *mtd = &this->mtd;
+	struct nand_chip *chip = &this->nand;
+	int ret;
+
+	/* init current chip */
+	this->current_chip	= -1;
+
+	/* init the MTD data structures */
+	mtd->priv		= chip;
+	mtd->name		= "gpmi-nand";
+	mtd->owner		= THIS_MODULE;
+
+	/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
+	chip->priv		= this;
+	chip->select_chip	= gpmi_select_chip;
+	chip->cmd_ctrl		= gpmi_cmd_ctrl;
+	chip->dev_ready		= gpmi_dev_ready;
+	chip->read_byte		= gpmi_read_byte;
+	chip->read_buf		= gpmi_read_buf;
+	chip->write_buf		= gpmi_write_buf;
+	chip->ecc.read_page	= gpmi_ecc_read_page;
+	chip->ecc.write_page	= gpmi_ecc_write_page;
+	chip->ecc.read_oob	= gpmi_ecc_read_oob;
+	chip->ecc.write_oob	= gpmi_ecc_write_oob;
+	chip->scan_bbt		= gpmi_scan_bbt;
+	chip->badblock_pattern	= &gpmi_bbt_descr;
+	chip->block_markbad	= gpmi_block_markbad;
+	chip->options		|= NAND_NO_SUBPAGE_WRITE;
+	chip->ecc.mode		= NAND_ECC_HW;
+	chip->ecc.size		= 1;
+	chip->ecc.layout	= &gpmi_hw_ecclayout;
+
+	/* Allocate a temporary DMA buffer for reading ID in the nand_scan() */
+	this->bch_geometry.payload_size_in_bytes	= 1024;
+	this->bch_geometry.auxiliary_size_in_bytes	= 128;
+	ret = gpmi_alloc_dma_buffer(this);
+	if (ret)
+		goto err_out;
+
+	ret = nand_scan(mtd, pdata->max_chip_count);
+	if (ret) {
+		pr_err("Chip scan failed\n");
+		goto err_out;
+	}
+
+	ret = mtd_device_parse_register(mtd, NULL, NULL,
+			pdata->partitions, pdata->partition_count);
+	if (ret)
+		goto err_out;
+	return 0;
+
+err_out:
+	gpmi_nfc_exit(this);
+	return ret;
+}
+
+static int __devinit gpmi_nand_probe(struct platform_device *pdev)
+{
+	struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct gpmi_nand_data *this;
+	int ret;
+
+	this = kzalloc(sizeof(*this), GFP_KERNEL);
+	if (!this) {
+		pr_err("Failed to allocate per-device memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, this);
+	this->pdev  = pdev;
+	this->dev   = &pdev->dev;
+	this->pdata = pdata;
+
+	if (pdata->platform_init) {
+		ret = pdata->platform_init();
+		if (ret)
+			goto platform_init_error;
+	}
+
+	ret = acquire_resources(this);
+	if (ret)
+		goto exit_acquire_resources;
+
+	ret = init_hardware(this);
+	if (ret)
+		goto exit_nfc_init;
+
+	ret = gpmi_nfc_init(this);
+	if (ret)
+		goto exit_nfc_init;
+
+	return 0;
+
+exit_nfc_init:
+	release_resources(this);
+platform_init_error:
+exit_acquire_resources:
+	platform_set_drvdata(pdev, NULL);
+	kfree(this);
+	return ret;
+}
+
+static int __exit gpmi_nand_remove(struct platform_device *pdev)
+{
+	struct gpmi_nand_data *this = platform_get_drvdata(pdev);
+
+	gpmi_nfc_exit(this);
+	release_resources(this);
+	platform_set_drvdata(pdev, NULL);
+	kfree(this);
+	return 0;
+}
+
+static const struct platform_device_id gpmi_ids[] = {
+	{
+		.name = "imx23-gpmi-nand",
+		.driver_data = IS_MX23,
+	}, {
+		.name = "imx28-gpmi-nand",
+		.driver_data = IS_MX28,
+	}, {},
+};
+
+static struct platform_driver gpmi_nand_driver = {
+	.driver = {
+		.name = "gpmi-nand",
+	},
+	.probe   = gpmi_nand_probe,
+	.remove  = __exit_p(gpmi_nand_remove),
+	.id_table = gpmi_ids,
+};
+
+static int __init gpmi_nand_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&gpmi_nand_driver);
+	if (err == 0)
+		printk(KERN_INFO "GPMI NAND driver registered. (IMX)\n");
+	else
+		pr_err("i.MX GPMI NAND driver registration failed\n");
+	return err;
+}
+
+static void __exit gpmi_nand_exit(void)
+{
+	platform_driver_unregister(&gpmi_nand_driver);
+}
+
+module_init(gpmi_nand_init);
+module_exit(gpmi_nand_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
new file mode 100644
index 0000000..e1cb2b8
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -0,0 +1,274 @@
+/*
+ * Freescale GPMI 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.
+ */
+#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H
+#define __DRIVERS_MTD_NAND_GPMI_NAND_H
+
+#include <linux/mtd/nand.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <mach/dma.h>
+
+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;
+	struct clk    *clock;
+};
+
+/**
+ * struct bch_geometry - BCH geometry description.
+ * @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 bch_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.
+ * @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  stride_size_in_pages;
+	unsigned int  search_area_stride_exponent;
+};
+
+/* DMA operations types */
+enum dma_ops_type {
+	DMA_FOR_COMMAND = 1,
+	DMA_FOR_READ_DATA,
+	DMA_FOR_WRITE_DATA,
+	DMA_FOR_READ_ECC_PAGE,
+	DMA_FOR_WRITE_ECC_PAGE
+};
+
+/**
+ * struct nand_timing - Fundamental timing attributes for NAND.
+ * @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_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_nand_data {
+	/* System Interface */
+	struct device		*dev;
+	struct platform_device	*pdev;
+	struct gpmi_nand_platform_data	*pdata;
+
+	/* Resources */
+	struct resources	resources;
+
+	/* Flash Hardware */
+	struct nand_timing	timing;
+
+	/* BCH */
+	struct bch_geometry	bch_geometry;
+	struct completion	bch_done;
+
+	/* NAND Boot issue */
+	bool			swap_block_mark;
+	struct boot_rom_geometry rom_geometry;
+
+	/* MTD / NAND */
+	struct nand_chip	nand;
+	struct mtd_info		mtd;
+
+	/* General-use Variables */
+	int			current_chip;
+	unsigned int		command_length;
+
+	/* passed from upper layer */
+	uint8_t			*upper_buf;
+	int			upper_len;
+
+	/* for DMA operations */
+	bool			direct_dma_map_ok;
+
+	struct scatterlist	cmd_sgl;
+	char			*cmd_buffer;
+
+	struct scatterlist	data_sgl;
+	char			*data_buffer_dma;
+
+	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;
+
+	/* DMA channels */
+#define DMA_CHANS		8
+	struct dma_chan		*dma_chans[DMA_CHANS];
+	struct mxs_dma_data	dma_data;
+	enum dma_ops_type	last_dma_type;
+	enum dma_ops_type	dma_type;
+	struct completion	dma_done;
+
+	/* private */
+	void			*private;
+};
+
+/**
+ * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
+ * @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 timing_threshod - Timing threshold
+ * @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.
+ * @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.
+ */
+struct timing_threshod {
+	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;
+	unsigned long           clock_frequency_in_hz;
+
+};
+
+/* Common Services */
+extern int common_nfc_set_geometry(struct gpmi_nand_data *);
+extern struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
+extern void prepare_data_dma(struct gpmi_nand_data *,
+				enum dma_data_direction dr);
+extern int start_dma_without_bch_irq(struct gpmi_nand_data *,
+				struct dma_async_tx_descriptor *);
+extern int start_dma_with_bch_irq(struct gpmi_nand_data *,
+				struct dma_async_tx_descriptor *);
+
+/* GPMI-NAND helper function library */
+extern int gpmi_init(struct gpmi_nand_data *);
+extern void gpmi_clear_bch(struct gpmi_nand_data *);
+extern void gpmi_dump_info(struct gpmi_nand_data *);
+extern int bch_set_geometry(struct gpmi_nand_data *);
+extern int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
+extern int gpmi_send_command(struct gpmi_nand_data *);
+extern void gpmi_begin(struct gpmi_nand_data *);
+extern void gpmi_end(struct gpmi_nand_data *);
+extern int gpmi_read_data(struct gpmi_nand_data *);
+extern int gpmi_send_data(struct gpmi_nand_data *);
+extern int gpmi_send_page(struct gpmi_nand_data *,
+			dma_addr_t payload, dma_addr_t auxiliary);
+extern int gpmi_read_page(struct gpmi_nand_data *,
+			dma_addr_t payload, dma_addr_t auxiliary);
+
+/* BCH : Status Block Completion Codes */
+#define STATUS_GOOD		0x00
+#define STATUS_ERASED		0xff
+#define STATUS_UNCORRECTABLE	0xfe
+
+/* Use the platform_id to distinguish different Archs. */
+#define IS_MX23			0x1
+#define IS_MX28			0x2
+#define GPMI_IS_MX23(x)		((x)->pdev->id_entry->driver_data == IS_MX23)
+#define GPMI_IS_MX28(x)		((x)->pdev->id_entry->driver_data == IS_MX28)
+#endif
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v10 1/3] MTD : add the common code for GPMI-NAND controller driver
@ 2011-08-24  7:33   ` Huang Shijie
  0 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-24  7:33 UTC (permalink / raw)
  To: linux-arm-kernel

These files contain the common code for the GPMI-NAND driver.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 1674 ++++++++++++++++++++++++++++++++
 drivers/mtd/nand/gpmi-nand/gpmi-nand.h |  274 ++++++
 2 files changed, 1948 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.c
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-nand.h

diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
new file mode 100644
index 0000000..99940bd
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -0,0 +1,1674 @@
+/*
+ * Freescale GPMI 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/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/gpmi-nand.h>
+#include <linux/mtd/partitions.h>
+
+#include "gpmi-nand.h"
+
+/* 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
+};
+
+/*  We will use all the (page + OOB). */
+static struct nand_ecclayout gpmi_hw_ecclayout = {
+	.eccbytes = 0,
+	.eccpos = { 0, },
+	.oobfree = { {.offset = 0, .length = 0} }
+};
+
+/* debug control */
+#define GPMI_DEBUG_READ_OOB	0x0001
+#define GPMI_DEBUG_READ		0x0002
+#define GPMI_DEBUG_WRITE	0x0004
+#define GPMI_DEBUG_ECC_READ	0x0008
+#define GPMI_DEBUG_ECC_WRITE	0x0010
+#define GPMI_DEBUG_VERBOSE	0x0020
+
+#define logio(level)				\
+		do {				\
+			if (gpmi_debug & level)	\
+				pr_info("%s : %d\n", __func__, __LINE__); \
+		} while (0)
+
+int gpmi_debug;
+module_param(gpmi_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(gpmi_debug, "print out the debug info.");
+
+static irqreturn_t bch_irq(int irq, void *cookie)
+{
+	struct gpmi_nand_data *this = cookie;
+
+	gpmi_clear_bch(this);
+	complete(&this->bch_done);
+	return IRQ_HANDLED;
+}
+
+/* calculate the ECC strength by hand */
+static inline int get_ecc_strength(struct gpmi_nand_data *this)
+{
+	struct mtd_info	*mtd = &this->mtd;
+	int ecc_strength = 0;
+
+	switch (mtd->writesize) {
+	case 2048:
+		ecc_strength = 8;
+		break;
+	case 4096:
+		switch (mtd->oobsize) {
+		case 128:
+			ecc_strength = 8;
+			break;
+		case 224:
+		case 218:
+			ecc_strength = 16;
+			break;
+		}
+		break;
+	case 8192:
+		ecc_strength = 24;
+		break;
+	}
+
+	return ecc_strength;
+}
+
+int common_nfc_set_geometry(struct gpmi_nand_data *this)
+{
+	struct bch_geometry *geo = &this->bch_geometry;
+	struct mtd_info *mtd = &this->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;
+	int gf_len = 13;/* use GP13 by default */
+
+	/* We only support BCH now. */
+	geo->ecc_algorithm = "BCH";
+
+	/*
+	 * The size of the metadata can be changed, though we set it to 10
+	 * bytes now. But it can't be too large, because we have to save
+	 * enough space for BCH.
+	 */
+	geo->metadata_size_in_bytes = 10;
+
+	/* ECC chunks, used by default. */
+	geo->ecc_chunk_size_in_bytes = 512;
+
+	/*
+	 * 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.
+	 */
+	geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size_in_bytes;
+
+	/*
+	 * We use the same ECC strength for all chunks, including the first one.
+	 */
+	geo->ecc_strength = get_ecc_strength(this);
+	if (!geo->ecc_strength) {
+		pr_err("Page size:%d, OOB:%d\n", mtd->writesize, mtd->oobsize);
+		return -EINVAL;
+	}
+
+	/* Compute the page size, include page and oob. */
+	geo->page_size_in_bytes = mtd->writesize + mtd->oobsize;
+	geo->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 = ALIGN(geo->metadata_size_in_bytes, 4);
+	status_size   = ALIGN(geo->ecc_chunk_count, 4);
+
+	geo->auxiliary_size_in_bytes = metadata_size + status_size;
+	geo->auxiliary_status_offset = metadata_size;
+
+	/* Check if we're going to do block mark swapping. */
+	if (!this->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 = geo->ecc_chunk_size_in_bytes * 8;
+	chunk_ecc_size_in_bits  = geo->ecc_strength * gf_len;
+	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 -= geo->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.
+		 */
+		pr_err("Unsupported page geometry : %u:%u\n",
+				mtd->writesize, mtd->oobsize);
+		return -EINVAL;
+	}
+
+	/*
+	 * 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.
+	 */
+	geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+	geo->block_mark_bit_offset  = block_mark_bit_offset % 8;
+
+	return 0;
+}
+
+struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
+{
+	int chipnr = this->current_chip;
+
+	BUG_ON(chipnr < 0);
+	return this->dma_chans[chipnr];
+}
+
+/* Can we use the upper's buffer directly for DMA? */
+void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr)
+{
+	struct scatterlist *sgl = &this->data_sgl;
+	int ret;
+
+	this->direct_dma_map_ok = true;
+
+	/* first try to map the upper buffer directly */
+	sg_init_one(sgl, this->upper_buf, this->upper_len);
+	ret = dma_map_sg(this->dev, sgl, 1, dr);
+	if (ret == 0) {
+		/* We have to use our own DMA buffer. */
+		sg_init_one(sgl, this->data_buffer_dma, PAGE_SIZE);
+
+		if (dr == DMA_TO_DEVICE)
+			memcpy(this->data_buffer_dma, this->upper_buf,
+				this->upper_len);
+
+		ret = dma_map_sg(this->dev, sgl, 1, dr);
+		BUG_ON(ret == 0);
+
+		this->direct_dma_map_ok = false;
+	}
+}
+
+/* This will be called after the DMA operation is finished. */
+static void dma_irq_callback(void *param)
+{
+	struct gpmi_nand_data *this = param;
+	struct completion *dma_c = &this->dma_done;
+
+	complete(dma_c);
+
+	switch (this->dma_type) {
+	case DMA_FOR_COMMAND:
+		dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE);
+		break;
+
+	case DMA_FOR_READ_DATA:
+		dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE);
+		if (this->direct_dma_map_ok == false)
+			memcpy(this->upper_buf, this->data_buffer_dma,
+				this->upper_len);
+		break;
+
+	case DMA_FOR_WRITE_DATA:
+		dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE);
+		break;
+
+	case DMA_FOR_READ_ECC_PAGE:
+	case DMA_FOR_WRITE_ECC_PAGE:
+		/* We have to wait the BCH interrupt to finish. */
+		break;
+
+	default:
+		BUG();
+	}
+}
+
+int start_dma_without_bch_irq(struct gpmi_nand_data *this,
+				struct dma_async_tx_descriptor *desc)
+{
+	struct completion *dma_c = &this->dma_done;
+	int err;
+
+	init_completion(dma_c);
+
+	desc->callback		= dma_irq_callback;
+	desc->callback_param	= this;
+	dmaengine_submit(desc);
+
+	/* Wait for the interrupt from the DMA block. */
+	err = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
+	err = (!err) ? -ETIMEDOUT : 0;
+	if (err) {
+		pr_err("DMA timeout, last DMA :%d\n", this->last_dma_type);
+		if (gpmi_debug & GPMI_DEBUG_VERBOSE)
+			gpmi_dump_info(this);
+	}
+	return err;
+}
+
+/*
+ * This function is used in BCH reading or BCH writing pages.
+ * It will wait for the BCH interrupt as long as ONE second.
+ * Actually, we must wait for two interrupts :
+ *	[1] firstly the DMA interrupt and
+ *	[2] secondly the BCH interrupt.
+ */
+int start_dma_with_bch_irq(struct gpmi_nand_data *this,
+			struct dma_async_tx_descriptor *desc)
+{
+	int err;
+
+	/* Prepare to receive an interrupt from the BCH block. */
+	init_completion(&this->bch_done);
+
+	/* start the DMA */
+	start_dma_without_bch_irq(this, desc);
+
+	/* Wait for the interrupt from the BCH block. */
+	err = wait_for_completion_timeout(&this->bch_done,
+					msecs_to_jiffies(1000));
+	err = (!err) ? -ETIMEDOUT : 0;
+	if (err) {
+		pr_err("BCH timeout!\n");
+		if (gpmi_debug & GPMI_DEBUG_VERBOSE)
+			gpmi_dump_info(this);
+	}
+	return err;
+}
+
+static int __devinit
+acquire_register_block(struct gpmi_nand_data *this, const char *res_name)
+{
+	struct platform_device *pdev = this->pdev;
+	struct resources *res = &this->resources;
+	struct resource *r;
+	void *p;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
+	if (!r) {
+		pr_err("Can't get resource for %s\n", res_name);
+		return -ENXIO;
+	}
+
+	p = ioremap(r->start, resource_size(r));
+	if (!p) {
+		pr_err("Can't remap %s\n", res_name);
+		return -ENOMEM;
+	}
+
+	if (res_name == GPMI_NAND_GPMI_REGS_ADDR_RES_NAME)
+		res->gpmi_regs = p;
+	else if (res_name == GPMI_NAND_BCH_REGS_ADDR_RES_NAME)
+		res->bch_regs = p;
+	else
+		BUG();
+
+	return 0;
+}
+
+static void release_register_block(struct gpmi_nand_data *this)
+{
+	struct resources *res = &this->resources;
+	if (res->gpmi_regs)
+		iounmap(res->gpmi_regs);
+	if (res->bch_regs)
+		iounmap(res->bch_regs);
+	res->gpmi_regs = NULL;
+	res->bch_regs = NULL;
+}
+
+static int __devinit
+acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
+{
+	struct platform_device *pdev = this->pdev;
+	struct resources *res = &this->resources;
+	const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME;
+	struct resource *r;
+	int err;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
+	if (!r) {
+		pr_err("Can't get resource for %s\n", res_name);
+		return -ENXIO;
+	}
+
+	BUG_ON(r->start != r->end);
+	err = request_irq(r->start, irq_h, 0, res_name, this);
+	if (err) {
+		pr_err("Can't own %s\n", res_name);
+		return err;
+	}
+
+	res->bch_low_interrupt = r->start;
+	res->bch_high_interrupt = r->end;
+	return 0;
+}
+
+static void release_bch_irq(struct gpmi_nand_data *this)
+{
+	struct resources *res = &this->resources;
+	int i = res->bch_low_interrupt;
+
+	for (; i <= res->bch_high_interrupt; i++)
+		free_irq(i, this);
+}
+
+static bool gpmi_dma_filter(struct dma_chan *chan, void *param)
+{
+	struct gpmi_nand_data *this = param;
+	struct resource *r = this->private;
+
+	if (!mxs_dma_is_apbh(chan))
+		return false;
+	/*
+	 * only catch the GPMI dma channels :
+	 *	for mx23 :	MX23_DMA_GPMI0 ~ MX23_DMA_GPMI3
+	 *		(These four channels share the same IRQ!)
+	 *
+	 *	for mx28 :	MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7
+	 *		(These eight channels share the same IRQ!)
+	 */
+	if (r->start <= chan->chan_id && chan->chan_id <= r->end) {
+		chan->private = &this->dma_data;
+		return true;
+	}
+	return false;
+}
+
+static void release_dma_channels(struct gpmi_nand_data *this)
+{
+	unsigned int i;
+	for (i = 0; i < DMA_CHANS; i++)
+		if (this->dma_chans[i]) {
+			dma_release_channel(this->dma_chans[i]);
+			this->dma_chans[i] = NULL;
+		}
+}
+
+static int __devinit acquire_dma_channels(struct gpmi_nand_data *this)
+{
+	struct platform_device *pdev = this->pdev;
+	struct gpmi_nand_platform_data *pdata = this->pdata;
+	struct resources *res = &this->resources;
+	struct resource *r, *r_dma;
+	unsigned int i;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+					GPMI_NAND_DMA_CHANNELS_RES_NAME);
+	r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+					GPMI_NAND_DMA_INTERRUPT_RES_NAME);
+	if (!r || !r_dma) {
+		pr_err("Can't get resource for DMA\n");
+		return -ENXIO;
+	}
+
+	/* used in gpmi_dma_filter() */
+	this->private = r;
+
+	for (i = r->start; i <= r->end; i++) {
+		struct dma_chan *dma_chan;
+		dma_cap_mask_t mask;
+
+		if (i - r->start >= pdata->max_chip_count)
+			break;
+
+		dma_cap_zero(mask);
+		dma_cap_set(DMA_SLAVE, mask);
+
+		/* get the DMA interrupt */
+		if (r_dma->start == r_dma->end) {
+			/* only register the first. */
+			if (i == r->start)
+				this->dma_data.chan_irq = r_dma->start;
+			else
+				this->dma_data.chan_irq = NO_IRQ;
+		} else
+			this->dma_data.chan_irq = r_dma->start + (i - r->start);
+
+		dma_chan = dma_request_channel(mask, gpmi_dma_filter, this);
+		if (!dma_chan)
+			goto acquire_err;
+
+		/* fill the first empty item */
+		this->dma_chans[i - r->start] = dma_chan;
+	}
+
+	res->dma_low_channel = r->start;
+	res->dma_high_channel = i;
+	return 0;
+
+acquire_err:
+	pr_err("Can't acquire DMA channel %u\n", i);
+	release_dma_channels(this);
+	return -EINVAL;
+}
+
+static int __devinit acquire_resources(struct gpmi_nand_data *this)
+{
+	struct resources *res = &this->resources;
+	int ret;
+
+	ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME);
+	if (ret)
+		goto exit_regs;
+
+	ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME);
+	if (ret)
+		goto exit_regs;
+
+	ret = acquire_bch_irq(this, bch_irq);
+	if (ret)
+		goto exit_regs;
+
+	ret = acquire_dma_channels(this);
+	if (ret)
+		goto exit_dma_channels;
+
+	res->clock = clk_get(&this->pdev->dev, NULL);
+	if (IS_ERR(res->clock)) {
+		pr_err("can not get the clock\n");
+		ret = -ENOENT;
+		goto exit_clock;
+	}
+	return 0;
+
+exit_clock:
+	release_dma_channels(this);
+exit_dma_channels:
+	release_bch_irq(this);
+exit_regs:
+	release_register_block(this);
+	return ret;
+}
+
+static void release_resources(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+
+	clk_put(r->clock);
+	release_register_block(this);
+	release_bch_irq(this);
+	release_dma_channels(this);
+}
+
+static int __devinit init_hardware(struct gpmi_nand_data *this)
+{
+	int ret;
+
+	/*
+	 * This structure contains the "safe" GPMI timing that should succeed
+	 * with any NAND Flash device
+	 * (although, with less-than-optimal performance).
+	 */
+	struct nand_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,
+	};
+
+	/* Initialize the hardwares. */
+	ret = gpmi_init(this);
+	if (ret)
+		return ret;
+
+	this->timing = safe_timing;
+	return 0;
+}
+
+static int read_page_prepare(struct gpmi_nand_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;
+
+	if (virt_addr_valid(destination)) {
+		dma_addr_t dest_phys;
+
+		dest_phys = dma_map_single(dev, destination,
+						length, DMA_FROM_DEVICE);
+		if (dma_mapping_error(dev, dest_phys)) {
+			if (alt_size < length) {
+				pr_err("Alternate buffer is too small\n");
+				return -ENOMEM;
+			}
+			goto map_failed;
+		}
+		*use_virt = destination;
+		*use_phys = dest_phys;
+		this->direct_dma_map_ok = true;
+		return 0;
+	}
+
+map_failed:
+	*use_virt = alt_virt;
+	*use_phys = alt_phys;
+	this->direct_dma_map_ok = false;
+	return 0;
+}
+
+static inline void read_page_end(struct gpmi_nand_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)
+{
+	if (this->direct_dma_map_ok)
+		dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE);
+}
+
+static inline void read_page_swap_end(struct gpmi_nand_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)
+{
+	if (!this->direct_dma_map_ok)
+		memcpy(destination, alt_virt, length);
+}
+
+static int send_page_prepare(struct gpmi_nand_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;
+
+	if (virt_addr_valid(source)) {
+		dma_addr_t source_phys;
+
+		source_phys = dma_map_single(dev, (void *)source, length,
+						DMA_TO_DEVICE);
+		if (dma_mapping_error(dev, source_phys)) {
+			if (alt_size < length) {
+				pr_err("Alternate buffer is too small\n");
+				return -ENOMEM;
+			}
+			goto map_failed;
+		}
+		*use_virt = source;
+		*use_phys = source_phys;
+		return 0;
+	}
+map_failed:
+	/*
+	 * Copy the content 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;
+	return 0;
+}
+
+static void send_page_end(struct gpmi_nand_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;
+	if (used_virt == source)
+		dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE);
+}
+
+static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
+{
+	struct device *dev = this->dev;
+
+	if (this->page_buffer_virt && virt_addr_valid(this->page_buffer_virt))
+		dma_free_coherent(dev, this->page_buffer_size,
+					this->page_buffer_virt,
+					this->page_buffer_phys);
+	kfree(this->cmd_buffer);
+	kfree(this->data_buffer_dma);
+
+	this->cmd_buffer		= NULL;
+	this->data_buffer_dma	= NULL;
+	this->page_buffer_virt	= NULL;
+	this->page_buffer_size	=  0;
+}
+
+/* Allocate the DMA buffers */
+static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
+{
+	struct bch_geometry *geo = &this->bch_geometry;
+	struct device *dev = this->dev;
+
+	/* [1] Allocate a command buffer. PAGE_SIZE is enough. */
+	this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA);
+	if (this->cmd_buffer == NULL)
+		goto error_alloc;
+
+	/* [2] Allocate a read/write data buffer. PAGE_SIZE is enough. */
+	this->data_buffer_dma = kzalloc(PAGE_SIZE, GFP_DMA);
+	if (this->data_buffer_dma == NULL)
+		goto error_alloc;
+
+	/*
+	 * [3] 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.
+	 */
+	this->page_buffer_size = geo->payload_size_in_bytes +
+				geo->auxiliary_size_in_bytes;
+
+	this->page_buffer_virt = dma_alloc_coherent(dev, this->page_buffer_size,
+					&this->page_buffer_phys, GFP_DMA);
+	if (!this->page_buffer_virt)
+		goto error_alloc;
+
+
+	/* Slice up the page buffer. */
+	this->payload_virt = this->page_buffer_virt;
+	this->payload_phys = this->page_buffer_phys;
+	this->auxiliary_virt = this->payload_virt + geo->payload_size_in_bytes;
+	this->auxiliary_phys = this->payload_phys + geo->payload_size_in_bytes;
+	return 0;
+
+error_alloc:
+	gpmi_free_dma_buffer(this);
+	pr_err("allocate DMA buffer ret!!\n");
+	return -ENOMEM;
+}
+
+static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+	int ret;
+
+	/*
+	 * 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. NAND_CMD_NONE means the END of the queue.
+	 */
+	if ((ctrl & (NAND_ALE | NAND_CLE))) {
+		if (data != NAND_CMD_NONE)
+			this->cmd_buffer[this->command_length++] = data;
+		return;
+	}
+
+	if (!this->command_length)
+		return;
+
+	ret = gpmi_send_command(this);
+	if (ret)
+		pr_err("Chip: %u, Error %d\n", this->current_chip, ret);
+
+	this->command_length = 0;
+}
+
+static int gpmi_dev_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+
+	return gpmi_is_ready(this, this->current_chip);
+}
+
+static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+
+	if ((this->current_chip < 0) && (chipnr >= 0))
+		gpmi_begin(this);
+	else if ((this->current_chip >= 0) && (chipnr < 0))
+		gpmi_end(this);
+
+	this->current_chip = chipnr;
+}
+
+static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+
+	logio(GPMI_DEBUG_READ);
+	this->upper_buf	= buf;
+	this->upper_len	= len;
+
+	gpmi_read_data(this);
+}
+
+static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+
+	logio(GPMI_DEBUG_WRITE);
+	this->upper_buf	= (uint8_t *)buf;
+	this->upper_len	= len;
+
+	gpmi_send_data(this);
+}
+
+static uint8_t gpmi_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+	uint8_t *buf = this->data_buffer_dma;
+
+	gpmi_read_buf(mtd, buf, 1);
+	return buf[0];
+}
+
+/*
+ * Handles block mark swapping.
+ * It can be called in swapping the block mark, or swapping it back,
+ * because the the operations are the same.
+ */
+static void block_mark_swapping(struct gpmi_nand_data *this,
+				void *payload, void *auxiliary)
+{
+	struct bch_geometry *nfc_geo = &this->bch_geometry;
+	unsigned char *p;
+	unsigned char *a;
+	unsigned int  bit;
+	unsigned char mask;
+	unsigned char from_data;
+	unsigned char from_oob;
+
+	if (!this->swap_block_mark)
+		return;
+
+	/*
+	 * If control arrives here, we're swapping. Make some convenience
+	 * variables.
+	 */
+	bit = nfc_geo->block_mark_bit_offset;
+	p   = 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));
+}
+
+static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf, int page)
+{
+	struct gpmi_nand_data *this = chip->priv;
+	struct bch_geometry *nfc_geo = &this->bch_geometry;
+	void          *payload_virt;
+	dma_addr_t    payload_phys;
+	void          *auxiliary_virt;
+	dma_addr_t    auxiliary_phys;
+	unsigned int  i;
+	unsigned char *status;
+	unsigned int  failed;
+	unsigned int  corrected;
+	int           ret;
+
+	logio(GPMI_DEBUG_ECC_READ);
+	ret = read_page_prepare(this, buf, mtd->writesize,
+					this->payload_virt, this->payload_phys,
+					nfc_geo->payload_size_in_bytes,
+					&payload_virt, &payload_phys);
+	if (ret) {
+		pr_err("Inadequate DMA buffer\n");
+		ret = -ENOMEM;
+		return ret;
+	}
+	auxiliary_virt = this->auxiliary_virt;
+	auxiliary_phys = this->auxiliary_phys;
+
+	/* go! */
+	ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
+	read_page_end(this, buf, mtd->writesize,
+			this->payload_virt, this->payload_phys,
+			nfc_geo->payload_size_in_bytes,
+			payload_virt, payload_phys);
+	if (ret) {
+		pr_err("Error in ECC-based read: %d\n", ret);
+		goto exit_nfc;
+	}
+
+	/* handle the block mark swapping */
+	block_mark_swapping(this, payload_virt, auxiliary_virt);
+
+	/* Loop over status bytes, accumulating ECC status. */
+	failed		= 0;
+	corrected	= 0;
+	status		= auxiliary_virt + nfc_geo->auxiliary_status_offset;
+
+	for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
+		if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
+			continue;
+
+		if (*status == STATUS_UNCORRECTABLE) {
+			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 gpmi_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(chip->oob_poi, ~0, mtd->oobsize);
+	chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
+
+	read_page_swap_end(this, buf, mtd->writesize,
+			this->payload_virt, this->payload_phys,
+			nfc_geo->payload_size_in_bytes,
+			payload_virt, payload_phys);
+exit_nfc:
+	return ret;
+}
+
+static void gpmi_ecc_write_page(struct mtd_info *mtd,
+				struct nand_chip *chip, const uint8_t *buf)
+{
+	struct gpmi_nand_data *this = chip->priv;
+	struct bch_geometry *nfc_geo = &this->bch_geometry;
+	const void *payload_virt;
+	dma_addr_t payload_phys;
+	const void *auxiliary_virt;
+	dma_addr_t auxiliary_phys;
+	int        ret;
+
+	logio(GPMI_DEBUG_ECC_WRITE);
+	if (this->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(this->payload_virt, buf, mtd->writesize);
+		payload_virt = this->payload_virt;
+		payload_phys = this->payload_phys;
+
+		memcpy(this->auxiliary_virt, chip->oob_poi,
+				nfc_geo->auxiliary_size_in_bytes);
+		auxiliary_virt = this->auxiliary_virt;
+		auxiliary_phys = this->auxiliary_phys;
+
+		/* Handle block mark swapping. */
+		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.
+		 */
+		ret = send_page_prepare(this,
+				buf, mtd->writesize,
+				this->payload_virt, this->payload_phys,
+				nfc_geo->payload_size_in_bytes,
+				&payload_virt, &payload_phys);
+		if (ret) {
+			pr_err("Inadequate payload DMA buffer\n");
+			return;
+		}
+
+		ret = send_page_prepare(this,
+				chip->oob_poi, mtd->oobsize,
+				this->auxiliary_virt, this->auxiliary_phys,
+				nfc_geo->auxiliary_size_in_bytes,
+				&auxiliary_virt, &auxiliary_phys);
+		if (ret) {
+			pr_err("Inadequate auxiliary DMA buffer\n");
+			goto exit_auxiliary;
+		}
+	}
+
+	/* Ask the NFC. */
+	ret = gpmi_send_page(this, payload_phys, auxiliary_phys);
+	if (ret)
+		pr_err("Error in ECC-based write: %d\n", ret);
+
+	if (!this->swap_block_mark) {
+		send_page_end(this, chip->oob_poi, mtd->oobsize,
+				this->auxiliary_virt, this->auxiliary_phys,
+				nfc_geo->auxiliary_size_in_bytes,
+				auxiliary_virt, auxiliary_phys);
+exit_auxiliary:
+		send_page_end(this, buf, mtd->writesize,
+				this->payload_virt, this->payload_phys,
+				nfc_geo->payload_size_in_bytes,
+				payload_virt, payload_phys);
+	}
+}
+
+/*
+ * 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.
+ *
+ * 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.
+ */
+static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+				int page, int sndcmd)
+{
+	struct gpmi_nand_data *this = chip->priv;
+
+	logio(GPMI_DEBUG_READ_OOB);
+	/* clear the OOB buffer */
+	memset(chip->oob_poi, ~0, mtd->oobsize);
+
+	/* Read out the conventional OOB. */
+	chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+	chip->read_buf(mtd, chip->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 (!this->swap_block_mark) {
+		/* Read the block mark into the first byte of the OOB buffer. */
+		chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+		chip->oob_poi[0] = chip->read_byte(mtd);
+	}
+
+	/*
+	 * Return true, indicating that the next call to this function must send
+	 * a command.
+	 */
+	return true;
+}
+
+static int
+gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
+{
+	/*
+	 * The BCH will use all the (page + oob).
+	 * Our gpmi_hw_ecclayout can only prohibit the JFFS2 to write the oob.
+	 * But it can not stop some ioctls such MEMWRITEOOB which uses
+	 * MTD_OOB_PLACE. So We have to implement this function to prohibit
+	 * these ioctls too.
+	 */
+	return -EPERM;
+}
+
+static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+	int block, ret = 0;
+	uint8_t *block_mark;
+	int column, page, status, chipnr;
+
+	/* Get block number */
+	block = (int)(ofs >> chip->bbt_erase_shift);
+	if (chip->bbt)
+		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+	/* Do we have a flash based bad block table ? */
+	if (chip->options & NAND_BBT_USE_FLASH)
+		ret = nand_update_bbt(mtd, ofs);
+	else {
+		chipnr = (int)(ofs >> chip->chip_shift);
+		chip->select_chip(mtd, chipnr);
+
+		column = this->swap_block_mark ? mtd->writesize : 0;
+
+		/* Write the block mark. */
+		block_mark = this->data_buffer_dma;
+		block_mark[0] = 0; /* bad block marker */
+
+		/* Shift to get page */
+		page = (int)(ofs >> chip->page_shift);
+
+		chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page);
+		chip->write_buf(mtd, block_mark, 1);
+		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+		status = chip->waitfunc(mtd, chip);
+		if (status & NAND_STATUS_FAIL)
+			ret = -EIO;
+
+		chip->select_chip(mtd, -1);
+	}
+	if (!ret)
+		mtd->ecc_stats.badblocks++;
+
+	return ret;
+}
+
+static int __devinit nand_boot_set_geometry(struct gpmi_nand_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 0;
+}
+
+static const char  *fingerprint = "STMP";
+static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this)
+{
+	struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+	struct device *dev = this->dev;
+	struct mtd_info *mtd = &this->mtd;
+	struct nand_chip *chip = &this->nand;
+	unsigned int search_area_size_in_strides;
+	unsigned int stride;
+	unsigned int page;
+	loff_t byte;
+	uint8_t *buffer = chip->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;
+
+	saved_chip_number = this->current_chip;
+	chip->select_chip(mtd, 0);
+
+	/*
+	 * Loop through the first search area, looking for the NCB fingerprint.
+	 */
+	dev_dbg(dev, "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;
+
+		dev_dbg(dev, "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.
+		 */
+		chip->cmdfunc(mtd, NAND_CMD_READ0, 12, page);
+		chip->read_buf(mtd, buffer, strlen(fingerprint));
+
+		/* Look for the fingerprint. */
+		if (!memcmp(buffer, fingerprint, strlen(fingerprint))) {
+			found_an_ncb_fingerprint = true;
+			break;
+		}
+
+	}
+
+	chip->select_chip(mtd, saved_chip_number);
+
+	if (found_an_ncb_fingerprint)
+		dev_dbg(dev, "\tFound a fingerprint\n");
+	else
+		dev_dbg(dev, "\tNo fingerprint found\n");
+	return found_an_ncb_fingerprint;
+}
+
+/* Writes a transcription stamp. */
+static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this)
+{
+	struct device *dev = this->dev;
+	struct boot_rom_geometry *rom_geo = &this->rom_geometry;
+	struct mtd_info *mtd = &this->mtd;
+	struct nand_chip *chip = &this->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 = chip->buffers->databuf;
+	int saved_chip_number;
+	int status;
+
+	/* Compute the search area geometry. */
+	block_size_in_pages = mtd->erasesize / mtd->writesize;
+	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;
+
+	dev_dbg(dev, "Search Area Geometry :\n");
+	dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks);
+	dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides);
+	dev_dbg(dev, "\tin Pages  : %u\n", search_area_size_in_pages);
+
+	/* Select chip 0. */
+	saved_chip_number = this->current_chip;
+	chip->select_chip(mtd, 0);
+
+	/* Loop over blocks in the first search area, erasing them. */
+	dev_dbg(dev, "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. */
+		dev_dbg(dev, "\tErasing block 0x%x\n", block);
+		chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+		chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+
+		/* Wait for the erase to finish. */
+		status = chip->waitfunc(mtd, chip);
+		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(chip->oob_poi, ~0, mtd->oobsize);
+	memcpy(buffer + 12, fingerprint, strlen(fingerprint));
+
+	/* Loop through the first search area, writing NCB fingerprints. */
+	dev_dbg(dev, "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. */
+		dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
+		chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+		chip->ecc.write_page_raw(mtd, chip, buffer);
+		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+		/* Wait for the write to finish. */
+		status = chip->waitfunc(mtd, chip);
+		if (status & NAND_STATUS_FAIL)
+			dev_err(dev, "[%s] Write failed.\n", __func__);
+	}
+
+	/* Deselect chip 0. */
+	chip->select_chip(mtd, saved_chip_number);
+	return 0;
+}
+
+static int __devinit mx23_boot_init(struct gpmi_nand_data  *this)
+{
+	struct device *dev = this->dev;
+	struct nand_chip *chip = &this->nand;
+	struct mtd_info *mtd = &this->mtd;
+	unsigned int block_count;
+	unsigned int block;
+	int     chipnr;
+	int     page;
+	loff_t  byte;
+	uint8_t block_mark;
+	int     ret = 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 (mx23_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.
+	 */
+	dev_dbg(dev, "Transcribing bad block marks...\n");
+
+	/* Compute the number of blocks in the entire medium. */
+	block_count = chip->chipsize >> chip->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.
+		 */
+		chipnr = block >> (chip->chip_shift - chip->phys_erase_shift);
+		page = block << (chip->phys_erase_shift - chip->page_shift);
+		byte = block <<  chip->phys_erase_shift;
+
+		/* Send the command to read the conventional block mark. */
+		chip->select_chip(mtd, chipnr);
+		chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+		block_mark = chip->read_byte(mtd);
+		chip->select_chip(mtd, -1);
+
+		/*
+		 * 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.
+		 */
+		if (block_mark != 0xff) {
+			dev_dbg(dev, "Transcribing mark in block %u\n", block);
+			ret = chip->block_markbad(mtd, byte);
+			if (ret)
+				dev_err(dev, "Failed to mark block bad with "
+							"ret %d\n", ret);
+		}
+	}
+
+	/* Write the stamp that indicates we've transcribed the block marks. */
+	mx23_write_transcription_stamp(this);
+	return 0;
+}
+
+static int __devinit nand_boot_init(struct gpmi_nand_data  *this)
+{
+	nand_boot_set_geometry(this);
+
+	/* This is ROM arch-specific initilization before the BBT scanning. */
+	if (GPMI_IS_MX23(this))
+		return mx23_boot_init(this);
+	return 0;
+}
+
+static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this)
+{
+	int ret;
+
+	/* Free the temporary DMA memory for reading ID. */
+	gpmi_free_dma_buffer(this);
+
+	/* Set up the NFC geometry which is used by BCH. */
+	ret = bch_set_geometry(this);
+	if (ret) {
+		pr_err("set geometry ret : %d\n", ret);
+		return ret;
+	}
+
+	/* Alloc the new DMA buffers according to the pagesize and oobsize */
+	return gpmi_alloc_dma_buffer(this);
+}
+
+static int gpmi_pre_bbt_scan(struct gpmi_nand_data  *this)
+{
+	int ret;
+
+	/* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
+	if (GPMI_IS_MX23(this))
+		this->swap_block_mark = false;
+	else
+		this->swap_block_mark = true;
+
+	/* Set up the medium geometry */
+	ret = gpmi_set_geometry(this);
+	if (ret)
+		return ret;
+
+	/* NAND boot init, depends on the gpmi_set_geometry(). */
+	return nand_boot_init(this);
+}
+
+static int gpmi_scan_bbt(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct gpmi_nand_data *this = chip->priv;
+	int ret;
+
+	/* Prepare for the BBT scan. */
+	ret = gpmi_pre_bbt_scan(this);
+	if (ret)
+		return ret;
+
+	/* use the default BBT implementation */
+	return nand_default_bbt(mtd);
+}
+
+void gpmi_nfc_exit(struct gpmi_nand_data *this)
+{
+	nand_release(&this->mtd);
+	gpmi_free_dma_buffer(this);
+}
+
+static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
+{
+	struct gpmi_nand_platform_data *pdata = this->pdata;
+	struct mtd_info  *mtd = &this->mtd;
+	struct nand_chip *chip = &this->nand;
+	int ret;
+
+	/* init current chip */
+	this->current_chip	= -1;
+
+	/* init the MTD data structures */
+	mtd->priv		= chip;
+	mtd->name		= "gpmi-nand";
+	mtd->owner		= THIS_MODULE;
+
+	/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
+	chip->priv		= this;
+	chip->select_chip	= gpmi_select_chip;
+	chip->cmd_ctrl		= gpmi_cmd_ctrl;
+	chip->dev_ready		= gpmi_dev_ready;
+	chip->read_byte		= gpmi_read_byte;
+	chip->read_buf		= gpmi_read_buf;
+	chip->write_buf		= gpmi_write_buf;
+	chip->ecc.read_page	= gpmi_ecc_read_page;
+	chip->ecc.write_page	= gpmi_ecc_write_page;
+	chip->ecc.read_oob	= gpmi_ecc_read_oob;
+	chip->ecc.write_oob	= gpmi_ecc_write_oob;
+	chip->scan_bbt		= gpmi_scan_bbt;
+	chip->badblock_pattern	= &gpmi_bbt_descr;
+	chip->block_markbad	= gpmi_block_markbad;
+	chip->options		|= NAND_NO_SUBPAGE_WRITE;
+	chip->ecc.mode		= NAND_ECC_HW;
+	chip->ecc.size		= 1;
+	chip->ecc.layout	= &gpmi_hw_ecclayout;
+
+	/* Allocate a temporary DMA buffer for reading ID in the nand_scan() */
+	this->bch_geometry.payload_size_in_bytes	= 1024;
+	this->bch_geometry.auxiliary_size_in_bytes	= 128;
+	ret = gpmi_alloc_dma_buffer(this);
+	if (ret)
+		goto err_out;
+
+	ret = nand_scan(mtd, pdata->max_chip_count);
+	if (ret) {
+		pr_err("Chip scan failed\n");
+		goto err_out;
+	}
+
+	ret = mtd_device_parse_register(mtd, NULL, NULL,
+			pdata->partitions, pdata->partition_count);
+	if (ret)
+		goto err_out;
+	return 0;
+
+err_out:
+	gpmi_nfc_exit(this);
+	return ret;
+}
+
+static int __devinit gpmi_nand_probe(struct platform_device *pdev)
+{
+	struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data;
+	struct gpmi_nand_data *this;
+	int ret;
+
+	this = kzalloc(sizeof(*this), GFP_KERNEL);
+	if (!this) {
+		pr_err("Failed to allocate per-device memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, this);
+	this->pdev  = pdev;
+	this->dev   = &pdev->dev;
+	this->pdata = pdata;
+
+	if (pdata->platform_init) {
+		ret = pdata->platform_init();
+		if (ret)
+			goto platform_init_error;
+	}
+
+	ret = acquire_resources(this);
+	if (ret)
+		goto exit_acquire_resources;
+
+	ret = init_hardware(this);
+	if (ret)
+		goto exit_nfc_init;
+
+	ret = gpmi_nfc_init(this);
+	if (ret)
+		goto exit_nfc_init;
+
+	return 0;
+
+exit_nfc_init:
+	release_resources(this);
+platform_init_error:
+exit_acquire_resources:
+	platform_set_drvdata(pdev, NULL);
+	kfree(this);
+	return ret;
+}
+
+static int __exit gpmi_nand_remove(struct platform_device *pdev)
+{
+	struct gpmi_nand_data *this = platform_get_drvdata(pdev);
+
+	gpmi_nfc_exit(this);
+	release_resources(this);
+	platform_set_drvdata(pdev, NULL);
+	kfree(this);
+	return 0;
+}
+
+static const struct platform_device_id gpmi_ids[] = {
+	{
+		.name = "imx23-gpmi-nand",
+		.driver_data = IS_MX23,
+	}, {
+		.name = "imx28-gpmi-nand",
+		.driver_data = IS_MX28,
+	}, {},
+};
+
+static struct platform_driver gpmi_nand_driver = {
+	.driver = {
+		.name = "gpmi-nand",
+	},
+	.probe   = gpmi_nand_probe,
+	.remove  = __exit_p(gpmi_nand_remove),
+	.id_table = gpmi_ids,
+};
+
+static int __init gpmi_nand_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&gpmi_nand_driver);
+	if (err == 0)
+		printk(KERN_INFO "GPMI NAND driver registered. (IMX)\n");
+	else
+		pr_err("i.MX GPMI NAND driver registration failed\n");
+	return err;
+}
+
+static void __exit gpmi_nand_exit(void)
+{
+	platform_driver_unregister(&gpmi_nand_driver);
+}
+
+module_init(gpmi_nand_init);
+module_exit(gpmi_nand_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
new file mode 100644
index 0000000..e1cb2b8
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -0,0 +1,274 @@
+/*
+ * Freescale GPMI 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.
+ */
+#ifndef __DRIVERS_MTD_NAND_GPMI_NAND_H
+#define __DRIVERS_MTD_NAND_GPMI_NAND_H
+
+#include <linux/mtd/nand.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <mach/dma.h>
+
+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;
+	struct clk    *clock;
+};
+
+/**
+ * struct bch_geometry - BCH geometry description.
+ * @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 bch_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.
+ * @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  stride_size_in_pages;
+	unsigned int  search_area_stride_exponent;
+};
+
+/* DMA operations types */
+enum dma_ops_type {
+	DMA_FOR_COMMAND = 1,
+	DMA_FOR_READ_DATA,
+	DMA_FOR_WRITE_DATA,
+	DMA_FOR_READ_ECC_PAGE,
+	DMA_FOR_WRITE_ECC_PAGE
+};
+
+/**
+ * struct nand_timing - Fundamental timing attributes for NAND.
+ * @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_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_nand_data {
+	/* System Interface */
+	struct device		*dev;
+	struct platform_device	*pdev;
+	struct gpmi_nand_platform_data	*pdata;
+
+	/* Resources */
+	struct resources	resources;
+
+	/* Flash Hardware */
+	struct nand_timing	timing;
+
+	/* BCH */
+	struct bch_geometry	bch_geometry;
+	struct completion	bch_done;
+
+	/* NAND Boot issue */
+	bool			swap_block_mark;
+	struct boot_rom_geometry rom_geometry;
+
+	/* MTD / NAND */
+	struct nand_chip	nand;
+	struct mtd_info		mtd;
+
+	/* General-use Variables */
+	int			current_chip;
+	unsigned int		command_length;
+
+	/* passed from upper layer */
+	uint8_t			*upper_buf;
+	int			upper_len;
+
+	/* for DMA operations */
+	bool			direct_dma_map_ok;
+
+	struct scatterlist	cmd_sgl;
+	char			*cmd_buffer;
+
+	struct scatterlist	data_sgl;
+	char			*data_buffer_dma;
+
+	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;
+
+	/* DMA channels */
+#define DMA_CHANS		8
+	struct dma_chan		*dma_chans[DMA_CHANS];
+	struct mxs_dma_data	dma_data;
+	enum dma_ops_type	last_dma_type;
+	enum dma_ops_type	dma_type;
+	struct completion	dma_done;
+
+	/* private */
+	void			*private;
+};
+
+/**
+ * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
+ * @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 timing_threshod - Timing threshold
+ * @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.
+ * @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.
+ */
+struct timing_threshod {
+	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;
+	unsigned long           clock_frequency_in_hz;
+
+};
+
+/* Common Services */
+extern int common_nfc_set_geometry(struct gpmi_nand_data *);
+extern struct dma_chan *get_dma_chan(struct gpmi_nand_data *);
+extern void prepare_data_dma(struct gpmi_nand_data *,
+				enum dma_data_direction dr);
+extern int start_dma_without_bch_irq(struct gpmi_nand_data *,
+				struct dma_async_tx_descriptor *);
+extern int start_dma_with_bch_irq(struct gpmi_nand_data *,
+				struct dma_async_tx_descriptor *);
+
+/* GPMI-NAND helper function library */
+extern int gpmi_init(struct gpmi_nand_data *);
+extern void gpmi_clear_bch(struct gpmi_nand_data *);
+extern void gpmi_dump_info(struct gpmi_nand_data *);
+extern int bch_set_geometry(struct gpmi_nand_data *);
+extern int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
+extern int gpmi_send_command(struct gpmi_nand_data *);
+extern void gpmi_begin(struct gpmi_nand_data *);
+extern void gpmi_end(struct gpmi_nand_data *);
+extern int gpmi_read_data(struct gpmi_nand_data *);
+extern int gpmi_send_data(struct gpmi_nand_data *);
+extern int gpmi_send_page(struct gpmi_nand_data *,
+			dma_addr_t payload, dma_addr_t auxiliary);
+extern int gpmi_read_page(struct gpmi_nand_data *,
+			dma_addr_t payload, dma_addr_t auxiliary);
+
+/* BCH : Status Block Completion Codes */
+#define STATUS_GOOD		0x00
+#define STATUS_ERASED		0xff
+#define STATUS_UNCORRECTABLE	0xfe
+
+/* Use the platform_id to distinguish different Archs. */
+#define IS_MX23			0x1
+#define IS_MX28			0x2
+#define GPMI_IS_MX23(x)		((x)->pdev->id_entry->driver_data == IS_MX23)
+#define GPMI_IS_MX28(x)		((x)->pdev->id_entry->driver_data == IS_MX28)
+#endif
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v10 2/3] MTD : add helper functions library and header files for GPMI NAND driver
  2011-08-24  7:33 ` Huang Shijie
@ 2011-08-24  7:33   ` Huang Shijie
  -1 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-24  7:33 UTC (permalink / raw)
  To: dedekind1
  Cc: koen.beel.barco, w.sang, Huang Shijie, linux-mtd, shijie8,
	s.hauer, linux-arm-kernel

bch-regs.h : registers file for BCH module
gpmi-regs.h: registers file for GPMI module
gpmi-lib.c: helper functions library.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/nand/gpmi-nand/bch-regs.h  |   84 +++
 drivers/mtd/nand/gpmi-nand/gpmi-lib.c  | 1056 ++++++++++++++++++++++++++++++++
 drivers/mtd/nand/gpmi-nand/gpmi-regs.h |  168 +++++
 3 files changed, 1308 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h

diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h
new file mode 100644
index 0000000..4effb8c
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h
@@ -0,0 +1,84 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright 2008-2011 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_NAND_BCH_REGS_H
+#define __GPMI_NAND_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_COMPLETE_IRQ_EN		(1 << 8)
+#define BM_BCH_CTRL_COMPLETE_IRQ		(1 << 0)
+
+#define HW_BCH_STATUS0				0x00000010
+#define HW_BCH_MODE				0x00000020
+#define HW_BCH_ENCODEPTR			0x00000030
+#define HW_BCH_DATAPTR				0x00000040
+#define HW_BCH_METAPTR				0x00000050
+#define HW_BCH_LAYOUTSELECT			0x00000070
+
+#define HW_BCH_FLASH0LAYOUT0			0x00000080
+
+#define BP_BCH_FLASH0LAYOUT0_NBLOCKS		24
+#define BM_BCH_FLASH0LAYOUT0_NBLOCKS	(0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v)		\
+	(((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS)
+
+#define BP_BCH_FLASH0LAYOUT0_META_SIZE		16
+#define BM_BCH_FLASH0LAYOUT0_META_SIZE	(0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v)	\
+	(((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\
+					 & BM_BCH_FLASH0LAYOUT0_META_SIZE)
+
+#define BP_BCH_FLASH0LAYOUT0_ECC0		12
+#define BM_BCH_FLASH0LAYOUT0_ECC0	(0xf << BP_BCH_FLASH0LAYOUT0_ECC0)
+#define BF_BCH_FLASH0LAYOUT0_ECC0(v)		\
+	(((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0)
+
+#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE		0
+#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE		\
+			(0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v)	\
+	(((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\
+					 & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+
+#define HW_BCH_FLASH0LAYOUT1			0x00000090
+
+#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE		16
+#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE		\
+			(0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v)	\
+	(((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \
+					 & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+
+#define BP_BCH_FLASH0LAYOUT1_ECCN		12
+#define BM_BCH_FLASH0LAYOUT1_ECCN	(0xf << BP_BCH_FLASH0LAYOUT1_ECCN)
+#define BF_BCH_FLASH0LAYOUT1_ECCN(v)		\
+	(((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN)
+
+#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE		0
+#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE		\
+			(0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v)	\
+	(((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
+					 & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+#endif
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
new file mode 100644
index 0000000..bbec946
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -0,0 +1,1056 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright (C) 2008-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/mtd/gpmi-nand.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <mach/mxs.h>
+
+#include "gpmi-nand.h"
+#include "gpmi-regs.h"
+#include "bch-regs.h"
+
+struct timing_threshod timing_default_threshold = {
+	.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,
+};
+
+/*
+ * Clear the bit and poll it cleared.  This is usually called with
+ * a reset address and mask being either SFTRST(bit 31) or CLKGATE
+ * (bit 30).
+ */
+static int clear_poll_bit(void __iomem *addr, u32 mask)
+{
+	int timeout = 0x400;
+
+	/* clear the bit */
+	__mxs_clrl(mask, addr);
+
+	/*
+	 * SFTRST needs 3 GPMI clocks to settle, the reference manual
+	 * recommends to wait 1us.
+	 */
+	udelay(1);
+
+	/* poll the bit becoming clear */
+	while ((readl(addr) & mask) && --timeout)
+		/* nothing */;
+
+	return !timeout;
+}
+
+#define MODULE_CLKGATE		(1 << 30)
+#define MODULE_SFTRST		(1 << 31)
+/*
+ * The current mxs_reset_block() will do two things:
+ *  [1] enable the module.
+ *  [2] reset the module.
+ *
+ * In most of the cases, it's ok. But there is a hardware bug in the BCH block.
+ * If you try to soft reset the BCH block, it becomes unusable until
+ * the next hard reset. This case occurs in the NAND boot mode. When the board
+ * boots by NAND, the ROM of the chip will initialize the BCH blocks itself.
+ * So If the driver tries to reset the BCH again, the BCH will not work anymore.
+ * You will see a DMA timeout in this case.
+ *
+ * To avoid this bug, just add a new parameter `just_enable` for
+ * the mxs_reset_block(), and rewrite it here.
+ */
+int gpmi_reset_block(void __iomem *reset_addr, bool just_enable)
+{
+	int ret;
+	int timeout = 0x400;
+
+	/* clear and poll SFTRST */
+	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+	if (unlikely(ret))
+		goto error;
+
+	/* clear CLKGATE */
+	__mxs_clrl(MODULE_CLKGATE, reset_addr);
+
+	if (!just_enable) {
+		/* set SFTRST to reset the block */
+		__mxs_setl(MODULE_SFTRST, reset_addr);
+		udelay(1);
+
+		/* poll CLKGATE becoming set */
+		while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout)
+			/* nothing */;
+		if (unlikely(!timeout))
+			goto error;
+	}
+
+	/* clear and poll SFTRST */
+	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+	if (unlikely(ret))
+		goto error;
+
+	/* clear and poll CLKGATE */
+	ret = clear_poll_bit(reset_addr, MODULE_CLKGATE);
+	if (unlikely(ret))
+		goto error;
+
+	return 0;
+
+error:
+	pr_err("%s(%p): module reset timeout\n", __func__, reset_addr);
+	return -ETIMEDOUT;
+}
+
+int gpmi_init(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	int ret;
+
+	ret = clk_enable(r->clock);
+	if (ret)
+		goto err_out;
+	ret = gpmi_reset_block(r->gpmi_regs, false);
+	if (ret)
+		goto err_out;
+
+	/* Choose NAND mode. */
+	writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+	/* Set the IRQ polarity. */
+	writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+				r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	/* Disable Write-Protection. */
+	writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	/* Select BCH ECC. */
+	writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	clk_disable(r->clock);
+	return 0;
+err_out:
+	return ret;
+}
+
+/* This function is very useful. It is called only when the bug occur. */
+void gpmi_dump_info(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	struct bch_geometry *geo = &this->bch_geometry;
+	u32 reg;
+	int i;
+
+	pr_err("Show GPMI registers :\n");
+	for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) {
+		reg = readl(r->gpmi_regs + i * 0x10);
+		pr_err("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
+	}
+
+	/* start to print out the BCH info */
+	pr_err("BCH Geometry :\n");
+	pr_err("ECC Algorithm          : %s\n", geo->ecc_algorithm);
+	pr_err("ECC Strength           : %u\n", geo->ecc_strength);
+	pr_err("Page Size in Bytes     : %u\n", geo->page_size_in_bytes);
+	pr_err("Metadata Size in Bytes : %u\n", geo->metadata_size_in_bytes);
+	pr_err("ECC Chunk Size in Bytes: %u\n", geo->ecc_chunk_size_in_bytes);
+	pr_err("ECC Chunk Count        : %u\n", geo->ecc_chunk_count);
+	pr_err("Payload Size in Bytes  : %u\n", geo->payload_size_in_bytes);
+	pr_err("Auxiliary Size in Bytes: %u\n", geo->auxiliary_size_in_bytes);
+	pr_err("Auxiliary Status Offset: %u\n", geo->auxiliary_status_offset);
+	pr_err("Block Mark Byte Offset : %u\n", geo->block_mark_byte_offset);
+	pr_err("Block Mark Bit Offset  : %u\n", geo->block_mark_bit_offset);
+}
+
+/* Configures the geometry for BCH.  */
+int bch_set_geometry(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	struct bch_geometry *bch_geo = &this->bch_geometry;
+	unsigned int block_count;
+	unsigned int block_size;
+	unsigned int metadata_size;
+	unsigned int ecc_strength;
+	unsigned int page_size;
+	int ret;
+
+	if (common_nfc_set_geometry(this))
+		return !0;
+
+	block_count   = bch_geo->ecc_chunk_count - 1;
+	block_size    = bch_geo->ecc_chunk_size_in_bytes;
+	metadata_size = bch_geo->metadata_size_in_bytes;
+	ecc_strength  = bch_geo->ecc_strength >> 1;
+	page_size     = bch_geo->page_size_in_bytes;
+
+	ret = clk_enable(r->clock);
+	if (ret)
+		goto err_out;
+
+	ret = gpmi_reset_block(r->bch_regs, true);
+	if (ret)
+		goto err_out;
+
+	/* Configure layout 0. */
+	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),
+			r->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+	writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size)
+			| BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength)
+			| BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size),
+			r->bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+	/* Set *all* chip selects to use layout 0. */
+	writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
+
+	/* Enable interrupts. */
+	writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
+				r->bch_regs + HW_BCH_CTRL_SET);
+
+	clk_disable(r->clock);
+	return 0;
+err_out:
+	return ret;
+}
+
+/* Converts time in nanoseconds to cycles. */
+static unsigned int ns_to_cycles(unsigned int time,
+			unsigned int period, unsigned int min)
+{
+	unsigned int k;
+
+	k = (time + period - 1) / period;
+	return max(k, min);
+}
+
+/* Apply timing to current hardware conditions. */
+static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
+					struct gpmi_nfc_hardware_timing *hw)
+{
+	struct gpmi_nand_platform_data *pdata = this->pdata;
+	struct timing_threshod *nfc = &timing_default_threshold;
+	struct nand_chip *nand = &this->nand;
+	struct nand_timing target = this->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;
+}
+
+/* Begin the I/O */
+void gpmi_begin(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	struct timing_threshod *nfc = &timing_default_threshold;
+	unsigned char  *gpmi_regs = r->gpmi_regs;
+	unsigned int   clock_period_in_ns;
+	uint32_t       reg;
+	unsigned int   dll_wait_time_in_us;
+	struct gpmi_nfc_hardware_timing  hw;
+	int ret;
+
+	/* Enable the clock. */
+	ret = clk_enable(r->clock);
+	if (ret) {
+		pr_err("We failed in enable the clk\n");
+		goto err_out;
+	}
+
+	/* set ready/busy timeout */
+	writel(0x500 << 16, gpmi_regs + HW_GPMI_TIMING1);
+
+	/* Get the timing information we need. */
+	nfc->clock_frequency_in_hz = clk_get_rate(r->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. */
+	reg = 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)       ;
+
+	writel(reg, gpmi_regs + HW_GPMI_TIMING0);
+
+	/*
+	 * DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD.
+	 */
+	writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+	/* Clear out the DLL control fields. */
+	writel(BM_GPMI_CTRL1_RDN_DELAY,   gpmi_regs + HW_GPMI_CTRL1_CLR);
+	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)
+		writel(BM_GPMI_CTRL1_HALF_PERIOD,
+						gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	/* Set the delay factor. */
+	writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor),
+						gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	/* Enable the DLL. */
+	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);
+
+err_out:
+	return;
+}
+
+void gpmi_end(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	clk_disable(r->clock);
+}
+
+/* Clears a BCH interrupt. */
+void gpmi_clear_bch(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR);
+}
+
+/* Returns the Ready/Busy status of the given chip. */
+int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
+{
+	struct resources *r = &this->resources;
+	uint32_t mask;
+	uint32_t reg;
+
+	if (GPMI_IS_MX23(this)) {
+		mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
+		reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
+	} else if (GPMI_IS_MX28(this)) {
+		mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip);
+		reg = readl(r->gpmi_regs + HW_GPMI_STAT);
+	} else
+		BUG();
+	return !!(reg & mask);
+}
+
+static inline void set_dma_type(struct gpmi_nand_data *this,
+					enum dma_ops_type type)
+{
+	this->last_dma_type = this->dma_type;
+	this->dma_type = type;
+}
+
+int gpmi_send_command(struct gpmi_nand_data *this)
+{
+	struct dma_chan *channel = get_dma_chan(this);
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist *sgl;
+	int chip = this->current_chip;
+	u32 pio[3];
+
+	/* [1] send out the PIO words */
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE)
+		| BM_GPMI_CTRL0_ADDRESS_INCREMENT
+		| BF_GPMI_CTRL0_XFER_COUNT(this->command_length);
+	pio[1] = pio[2] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 1 error\n");
+		return -1;
+	}
+
+	/* [2] send out the COMMAND + ADDRESS string stored in @buffer */
+	sgl = &this->cmd_sgl;
+
+	sg_init_one(sgl, this->cmd_buffer, this->command_length);
+	dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
+	desc = channel->device->device_prep_slave_sg(channel,
+					sgl, 1, DMA_TO_DEVICE, 1);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+
+	/* [3] submit the DMA */
+	set_dma_type(this, DMA_FOR_COMMAND);
+	return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_send_data(struct gpmi_nand_data *this)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = get_dma_chan(this);
+	int chip = this->current_chip;
+	uint32_t command_mode;
+	uint32_t address;
+	u32 pio[2];
+
+	/* [1] PIO */
+	command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+	address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+	pio[1] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 1 error\n");
+		return -1;
+	}
+
+	/* [2] send DMA request */
+	prepare_data_dma(this, DMA_TO_DEVICE);
+	desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl,
+						1, DMA_TO_DEVICE, 1);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+	/* [3] submit the DMA */
+	set_dma_type(this, DMA_FOR_WRITE_DATA);
+	return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_read_data(struct gpmi_nand_data *this)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = get_dma_chan(this);
+	int chip = this->current_chip;
+	u32 pio[2];
+
+	/* [1] : send PIO */
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
+		| BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+	pio[1] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 1 error\n");
+		return -1;
+	}
+
+	/* [2] : send DMA request */
+	prepare_data_dma(this, DMA_FROM_DEVICE);
+	desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl,
+						1, DMA_FROM_DEVICE, 1);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+
+	/* [3] : submit the DMA */
+	set_dma_type(this, DMA_FOR_READ_DATA);
+	return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_send_page(struct gpmi_nand_data *this,
+			dma_addr_t payload, dma_addr_t auxiliary)
+{
+	struct bch_geometry *geo = &this->bch_geometry;
+	uint32_t command_mode;
+	uint32_t address;
+	uint32_t ecc_command;
+	uint32_t buffer_mask;
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = get_dma_chan(this);
+	int chip = this->current_chip;
+	u32 pio[6];
+
+	/* 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;
+
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(0);
+	pio[1] = 0;
+	pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
+		| BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+		| BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+	pio[3] = geo->page_size_in_bytes;
+	pio[4] = payload;
+	pio[5] = auxiliary;
+
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+	set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE);
+	return start_dma_with_bch_irq(this, desc);
+}
+
+int gpmi_read_page(struct gpmi_nand_data *this,
+				dma_addr_t payload, dma_addr_t auxiliary)
+{
+	struct bch_geometry *geo = &this->bch_geometry;
+	uint32_t command_mode;
+	uint32_t address;
+	uint32_t ecc_command;
+	uint32_t buffer_mask;
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = get_dma_chan(this);
+	int chip = this->current_chip;
+	u32 pio[6];
+
+	/* [1] Wait for the chip to report ready. */
+	command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+	address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+	pio[0] =  BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(0);
+	pio[1] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+				(struct scatterlist *)pio, 2, DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 1 error\n");
+		return -1;
+	}
+
+	/* [2] 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;
+
+	pio[0] =  BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes);
+
+	pio[1] = 0;
+	pio[2] =  BM_GPMI_ECCCTRL_ENABLE_ECC
+		| BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+		| BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+	pio[3] = geo->page_size_in_bytes;
+	pio[4] = payload;
+	pio[5] = auxiliary;
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 1);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+
+	/* [3] Disable the BCH block */
+	command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+	address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes);
+	pio[1] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+				(struct scatterlist *)pio, 2, DMA_NONE, 1);
+	if (!desc) {
+		pr_err("step 3 error\n");
+		return -1;
+	}
+
+	/* [4] submit the DMA */
+	set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
+	return start_dma_with_bch_irq(this, desc);
+}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
new file mode 100644
index 0000000..4102050
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
@@ -0,0 +1,168 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright 2008-2011 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_NAND_GPMI_REGS_H
+#define __GPMI_NAND_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 BP_GPMI_CTRL0_COMMAND_MODE			24
+#define BM_GPMI_CTRL0_COMMAND_MODE	(3 << BP_GPMI_CTRL0_COMMAND_MODE)
+#define BF_GPMI_CTRL0_COMMAND_MODE(v)	\
+	(((v) << BP_GPMI_CTRL0_COMMAND_MODE) & 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			(1 << 23)
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT		0x0
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT		0x1
+
+/*
+ *  Difference in LOCK_CS between imx23 and imx28 :
+ *  This bit may impact the _POWER_ consumption. So some chips
+ *  do not set it.
+ */
+#define MX23_BP_GPMI_CTRL0_LOCK_CS			22
+#define MX28_BP_GPMI_CTRL0_LOCK_CS			27
+#define LOCK_CS_ENABLE					0x1
+#define BF_GPMI_CTRL0_LOCK_CS(v, x)			0x0
+
+/* Difference in CS between imx23 and imx28 */
+#define BP_GPMI_CTRL0_CS				20
+#define MX23_BM_GPMI_CTRL0_CS		(3 << BP_GPMI_CTRL0_CS)
+#define MX28_BM_GPMI_CTRL0_CS		(7 << BP_GPMI_CTRL0_CS)
+#define BF_GPMI_CTRL0_CS(v, x)		(((v) << BP_GPMI_CTRL0_CS) & \
+						(GPMI_IS_MX23((x)) \
+						? MX23_BM_GPMI_CTRL0_CS	\
+						: MX28_BM_GPMI_CTRL0_CS))
+
+#define BP_GPMI_CTRL0_ADDRESS				17
+#define BM_GPMI_CTRL0_ADDRESS		(3 << BP_GPMI_CTRL0_ADDRESS)
+#define BF_GPMI_CTRL0_ADDRESS(v)	\
+		(((v) << BP_GPMI_CTRL0_ADDRESS) & 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			(1 << 16)
+#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	(0xffff << BP_GPMI_CTRL0_XFER_COUNT)
+#define BF_GPMI_CTRL0_XFER_COUNT(v)	\
+		(((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT)
+
+#define HW_GPMI_COMPARE					0x00000010
+
+#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_ECC_CMD				13
+#define BM_GPMI_ECCCTRL_ECC_CMD		(3 << BP_GPMI_ECCCTRL_ECC_CMD)
+#define BF_GPMI_ECCCTRL_ECC_CMD(v)	\
+		(((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD)
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE		0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE		0x1
+
+#define BM_GPMI_ECCCTRL_ENABLE_ECC			(1 << 12)
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE		0x1
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE		0x0
+
+#define BP_GPMI_ECCCTRL_BUFFER_MASK			0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK	(0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK)
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v)	\
+	(((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & 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 HW_GPMI_PAYLOAD					0x00000040
+#define HW_GPMI_AUXILIARY				0x00000050
+#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_BCH_MODE				(1 << 18)
+
+#define BP_GPMI_CTRL1_DLL_ENABLE			17
+#define BM_GPMI_CTRL1_DLL_ENABLE	(1 << BP_GPMI_CTRL1_DLL_ENABLE)
+
+#define BP_GPMI_CTRL1_HALF_PERIOD			16
+#define BM_GPMI_CTRL1_HALF_PERIOD	(1 << BP_GPMI_CTRL1_HALF_PERIOD)
+
+#define BP_GPMI_CTRL1_RDN_DELAY				12
+#define BM_GPMI_CTRL1_RDN_DELAY		(0xf << BP_GPMI_CTRL1_RDN_DELAY)
+#define BF_GPMI_CTRL1_RDN_DELAY(v)	\
+		(((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY)
+
+#define BM_GPMI_CTRL1_DEV_RESET				(1 << 3)
+#define BV_GPMI_CTRL1_DEV_RESET__ENABLED		0x0
+#define BV_GPMI_CTRL1_DEV_RESET__DISABLED		0x1
+
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY		(1 << 2)
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW	0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH	0x1
+
+#define BM_GPMI_CTRL1_CAMERA_MODE			(1 << 1)
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND			0x0
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA			0x1
+
+#define BM_GPMI_CTRL1_GPMI_MODE				(1 << 0)
+
+#define HW_GPMI_TIMING0					0x00000070
+
+#define BP_GPMI_TIMING0_ADDRESS_SETUP			16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP	(0xff << BP_GPMI_TIMING0_ADDRESS_SETUP)
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v)	\
+	(((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+
+#define BP_GPMI_TIMING0_DATA_HOLD			8
+#define BM_GPMI_TIMING0_DATA_HOLD	(0xff << BP_GPMI_TIMING0_DATA_HOLD)
+#define BF_GPMI_TIMING0_DATA_HOLD(v)		\
+	(((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD)
+
+#define BP_GPMI_TIMING0_DATA_SETUP			0
+#define BM_GPMI_TIMING0_DATA_SETUP	(0xff << BP_GPMI_TIMING0_DATA_SETUP)
+#define BF_GPMI_TIMING0_DATA_SETUP(v)		\
+	(((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP)
+
+#define HW_GPMI_TIMING1					0x00000080
+#define HW_GPMI_TIMING2					0x00000090
+#define HW_GPMI_DATA					0x000000a0
+/*============================ MX28 uses this to detect READY ==============*/
+#define HW_GPMI_STAT					0x000000b0
+#define MX28_BP_GPMI_STAT_READY_BUSY			24
+#define MX28_BM_GPMI_STAT_READY_BUSY	(0xff << MX28_BP_GPMI_STAT_READY_BUSY)
+#define MX28_BF_GPMI_STAT_READY_BUSY(v)		\
+	(((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY)
+/*============================ MX23 uses this to detect READY ==============*/
+#define HW_GPMI_DEBUG					0x000000c0
+#define MX23_BP_GPMI_DEBUG_READY0			28
+#define MX23_BM_GPMI_DEBUG_READY0	(1 << MX23_BP_GPMI_DEBUG_READY0)
+#endif
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v10 2/3] MTD : add helper functions library and header files for GPMI NAND driver
@ 2011-08-24  7:33   ` Huang Shijie
  0 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-24  7:33 UTC (permalink / raw)
  To: linux-arm-kernel

bch-regs.h : registers file for BCH module
gpmi-regs.h: registers file for GPMI module
gpmi-lib.c: helper functions library.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/nand/gpmi-nand/bch-regs.h  |   84 +++
 drivers/mtd/nand/gpmi-nand/gpmi-lib.c  | 1056 ++++++++++++++++++++++++++++++++
 drivers/mtd/nand/gpmi-nand/gpmi-regs.h |  168 +++++
 3 files changed, 1308 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/gpmi-nand/bch-regs.h
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-lib.c
 create mode 100644 drivers/mtd/nand/gpmi-nand/gpmi-regs.h

diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h
new file mode 100644
index 0000000..4effb8c
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h
@@ -0,0 +1,84 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright 2008-2011 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_NAND_BCH_REGS_H
+#define __GPMI_NAND_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_COMPLETE_IRQ_EN		(1 << 8)
+#define BM_BCH_CTRL_COMPLETE_IRQ		(1 << 0)
+
+#define HW_BCH_STATUS0				0x00000010
+#define HW_BCH_MODE				0x00000020
+#define HW_BCH_ENCODEPTR			0x00000030
+#define HW_BCH_DATAPTR				0x00000040
+#define HW_BCH_METAPTR				0x00000050
+#define HW_BCH_LAYOUTSELECT			0x00000070
+
+#define HW_BCH_FLASH0LAYOUT0			0x00000080
+
+#define BP_BCH_FLASH0LAYOUT0_NBLOCKS		24
+#define BM_BCH_FLASH0LAYOUT0_NBLOCKS	(0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v)		\
+	(((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS)
+
+#define BP_BCH_FLASH0LAYOUT0_META_SIZE		16
+#define BM_BCH_FLASH0LAYOUT0_META_SIZE	(0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v)	\
+	(((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\
+					 & BM_BCH_FLASH0LAYOUT0_META_SIZE)
+
+#define BP_BCH_FLASH0LAYOUT0_ECC0		12
+#define BM_BCH_FLASH0LAYOUT0_ECC0	(0xf << BP_BCH_FLASH0LAYOUT0_ECC0)
+#define BF_BCH_FLASH0LAYOUT0_ECC0(v)		\
+	(((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0)
+
+#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE		0
+#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE		\
+			(0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v)	\
+	(((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\
+					 & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+
+#define HW_BCH_FLASH0LAYOUT1			0x00000090
+
+#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE		16
+#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE		\
+			(0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v)	\
+	(((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \
+					 & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+
+#define BP_BCH_FLASH0LAYOUT1_ECCN		12
+#define BM_BCH_FLASH0LAYOUT1_ECCN	(0xf << BP_BCH_FLASH0LAYOUT1_ECCN)
+#define BF_BCH_FLASH0LAYOUT1_ECCN(v)		\
+	(((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN)
+
+#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE		0
+#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE		\
+			(0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v)	\
+	(((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
+					 & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+#endif
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
new file mode 100644
index 0000000..bbec946
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -0,0 +1,1056 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright (C) 2008-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/mtd/gpmi-nand.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <mach/mxs.h>
+
+#include "gpmi-nand.h"
+#include "gpmi-regs.h"
+#include "bch-regs.h"
+
+struct timing_threshod timing_default_threshold = {
+	.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,
+};
+
+/*
+ * Clear the bit and poll it cleared.  This is usually called with
+ * a reset address and mask being either SFTRST(bit 31) or CLKGATE
+ * (bit 30).
+ */
+static int clear_poll_bit(void __iomem *addr, u32 mask)
+{
+	int timeout = 0x400;
+
+	/* clear the bit */
+	__mxs_clrl(mask, addr);
+
+	/*
+	 * SFTRST needs 3 GPMI clocks to settle, the reference manual
+	 * recommends to wait 1us.
+	 */
+	udelay(1);
+
+	/* poll the bit becoming clear */
+	while ((readl(addr) & mask) && --timeout)
+		/* nothing */;
+
+	return !timeout;
+}
+
+#define MODULE_CLKGATE		(1 << 30)
+#define MODULE_SFTRST		(1 << 31)
+/*
+ * The current mxs_reset_block() will do two things:
+ *  [1] enable the module.
+ *  [2] reset the module.
+ *
+ * In most of the cases, it's ok. But there is a hardware bug in the BCH block.
+ * If you try to soft reset the BCH block, it becomes unusable until
+ * the next hard reset. This case occurs in the NAND boot mode. When the board
+ * boots by NAND, the ROM of the chip will initialize the BCH blocks itself.
+ * So If the driver tries to reset the BCH again, the BCH will not work anymore.
+ * You will see a DMA timeout in this case.
+ *
+ * To avoid this bug, just add a new parameter `just_enable` for
+ * the mxs_reset_block(), and rewrite it here.
+ */
+int gpmi_reset_block(void __iomem *reset_addr, bool just_enable)
+{
+	int ret;
+	int timeout = 0x400;
+
+	/* clear and poll SFTRST */
+	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+	if (unlikely(ret))
+		goto error;
+
+	/* clear CLKGATE */
+	__mxs_clrl(MODULE_CLKGATE, reset_addr);
+
+	if (!just_enable) {
+		/* set SFTRST to reset the block */
+		__mxs_setl(MODULE_SFTRST, reset_addr);
+		udelay(1);
+
+		/* poll CLKGATE becoming set */
+		while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout)
+			/* nothing */;
+		if (unlikely(!timeout))
+			goto error;
+	}
+
+	/* clear and poll SFTRST */
+	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+	if (unlikely(ret))
+		goto error;
+
+	/* clear and poll CLKGATE */
+	ret = clear_poll_bit(reset_addr, MODULE_CLKGATE);
+	if (unlikely(ret))
+		goto error;
+
+	return 0;
+
+error:
+	pr_err("%s(%p): module reset timeout\n", __func__, reset_addr);
+	return -ETIMEDOUT;
+}
+
+int gpmi_init(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	int ret;
+
+	ret = clk_enable(r->clock);
+	if (ret)
+		goto err_out;
+	ret = gpmi_reset_block(r->gpmi_regs, false);
+	if (ret)
+		goto err_out;
+
+	/* Choose NAND mode. */
+	writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+	/* Set the IRQ polarity. */
+	writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+				r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	/* Disable Write-Protection. */
+	writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	/* Select BCH ECC. */
+	writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	clk_disable(r->clock);
+	return 0;
+err_out:
+	return ret;
+}
+
+/* This function is very useful. It is called only when the bug occur. */
+void gpmi_dump_info(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	struct bch_geometry *geo = &this->bch_geometry;
+	u32 reg;
+	int i;
+
+	pr_err("Show GPMI registers :\n");
+	for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) {
+		reg = readl(r->gpmi_regs + i * 0x10);
+		pr_err("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
+	}
+
+	/* start to print out the BCH info */
+	pr_err("BCH Geometry :\n");
+	pr_err("ECC Algorithm          : %s\n", geo->ecc_algorithm);
+	pr_err("ECC Strength           : %u\n", geo->ecc_strength);
+	pr_err("Page Size in Bytes     : %u\n", geo->page_size_in_bytes);
+	pr_err("Metadata Size in Bytes : %u\n", geo->metadata_size_in_bytes);
+	pr_err("ECC Chunk Size in Bytes: %u\n", geo->ecc_chunk_size_in_bytes);
+	pr_err("ECC Chunk Count        : %u\n", geo->ecc_chunk_count);
+	pr_err("Payload Size in Bytes  : %u\n", geo->payload_size_in_bytes);
+	pr_err("Auxiliary Size in Bytes: %u\n", geo->auxiliary_size_in_bytes);
+	pr_err("Auxiliary Status Offset: %u\n", geo->auxiliary_status_offset);
+	pr_err("Block Mark Byte Offset : %u\n", geo->block_mark_byte_offset);
+	pr_err("Block Mark Bit Offset  : %u\n", geo->block_mark_bit_offset);
+}
+
+/* Configures the geometry for BCH.  */
+int bch_set_geometry(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	struct bch_geometry *bch_geo = &this->bch_geometry;
+	unsigned int block_count;
+	unsigned int block_size;
+	unsigned int metadata_size;
+	unsigned int ecc_strength;
+	unsigned int page_size;
+	int ret;
+
+	if (common_nfc_set_geometry(this))
+		return !0;
+
+	block_count   = bch_geo->ecc_chunk_count - 1;
+	block_size    = bch_geo->ecc_chunk_size_in_bytes;
+	metadata_size = bch_geo->metadata_size_in_bytes;
+	ecc_strength  = bch_geo->ecc_strength >> 1;
+	page_size     = bch_geo->page_size_in_bytes;
+
+	ret = clk_enable(r->clock);
+	if (ret)
+		goto err_out;
+
+	ret = gpmi_reset_block(r->bch_regs, true);
+	if (ret)
+		goto err_out;
+
+	/* Configure layout 0. */
+	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),
+			r->bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+	writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size)
+			| BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength)
+			| BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size),
+			r->bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+	/* Set *all* chip selects to use layout 0. */
+	writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
+
+	/* Enable interrupts. */
+	writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
+				r->bch_regs + HW_BCH_CTRL_SET);
+
+	clk_disable(r->clock);
+	return 0;
+err_out:
+	return ret;
+}
+
+/* Converts time in nanoseconds to cycles. */
+static unsigned int ns_to_cycles(unsigned int time,
+			unsigned int period, unsigned int min)
+{
+	unsigned int k;
+
+	k = (time + period - 1) / period;
+	return max(k, min);
+}
+
+/* Apply timing to current hardware conditions. */
+static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
+					struct gpmi_nfc_hardware_timing *hw)
+{
+	struct gpmi_nand_platform_data *pdata = this->pdata;
+	struct timing_threshod *nfc = &timing_default_threshold;
+	struct nand_chip *nand = &this->nand;
+	struct nand_timing target = this->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;
+}
+
+/* Begin the I/O */
+void gpmi_begin(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	struct timing_threshod *nfc = &timing_default_threshold;
+	unsigned char  *gpmi_regs = r->gpmi_regs;
+	unsigned int   clock_period_in_ns;
+	uint32_t       reg;
+	unsigned int   dll_wait_time_in_us;
+	struct gpmi_nfc_hardware_timing  hw;
+	int ret;
+
+	/* Enable the clock. */
+	ret = clk_enable(r->clock);
+	if (ret) {
+		pr_err("We failed in enable the clk\n");
+		goto err_out;
+	}
+
+	/* set ready/busy timeout */
+	writel(0x500 << 16, gpmi_regs + HW_GPMI_TIMING1);
+
+	/* Get the timing information we need. */
+	nfc->clock_frequency_in_hz = clk_get_rate(r->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. */
+	reg = 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)       ;
+
+	writel(reg, gpmi_regs + HW_GPMI_TIMING0);
+
+	/*
+	 * DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD.
+	 */
+	writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
+
+	/* Clear out the DLL control fields. */
+	writel(BM_GPMI_CTRL1_RDN_DELAY,   gpmi_regs + HW_GPMI_CTRL1_CLR);
+	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)
+		writel(BM_GPMI_CTRL1_HALF_PERIOD,
+						gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	/* Set the delay factor. */
+	writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor),
+						gpmi_regs + HW_GPMI_CTRL1_SET);
+
+	/* Enable the DLL. */
+	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);
+
+err_out:
+	return;
+}
+
+void gpmi_end(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	clk_disable(r->clock);
+}
+
+/* Clears a BCH interrupt. */
+void gpmi_clear_bch(struct gpmi_nand_data *this)
+{
+	struct resources *r = &this->resources;
+	writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR);
+}
+
+/* Returns the Ready/Busy status of the given chip. */
+int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
+{
+	struct resources *r = &this->resources;
+	uint32_t mask;
+	uint32_t reg;
+
+	if (GPMI_IS_MX23(this)) {
+		mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
+		reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
+	} else if (GPMI_IS_MX28(this)) {
+		mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip);
+		reg = readl(r->gpmi_regs + HW_GPMI_STAT);
+	} else
+		BUG();
+	return !!(reg & mask);
+}
+
+static inline void set_dma_type(struct gpmi_nand_data *this,
+					enum dma_ops_type type)
+{
+	this->last_dma_type = this->dma_type;
+	this->dma_type = type;
+}
+
+int gpmi_send_command(struct gpmi_nand_data *this)
+{
+	struct dma_chan *channel = get_dma_chan(this);
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist *sgl;
+	int chip = this->current_chip;
+	u32 pio[3];
+
+	/* [1] send out the PIO words */
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE)
+		| BM_GPMI_CTRL0_ADDRESS_INCREMENT
+		| BF_GPMI_CTRL0_XFER_COUNT(this->command_length);
+	pio[1] = pio[2] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 1 error\n");
+		return -1;
+	}
+
+	/* [2] send out the COMMAND + ADDRESS string stored in @buffer */
+	sgl = &this->cmd_sgl;
+
+	sg_init_one(sgl, this->cmd_buffer, this->command_length);
+	dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
+	desc = channel->device->device_prep_slave_sg(channel,
+					sgl, 1, DMA_TO_DEVICE, 1);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+
+	/* [3] submit the DMA */
+	set_dma_type(this, DMA_FOR_COMMAND);
+	return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_send_data(struct gpmi_nand_data *this)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = get_dma_chan(this);
+	int chip = this->current_chip;
+	uint32_t command_mode;
+	uint32_t address;
+	u32 pio[2];
+
+	/* [1] PIO */
+	command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+	address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+	pio[1] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 1 error\n");
+		return -1;
+	}
+
+	/* [2] send DMA request */
+	prepare_data_dma(this, DMA_TO_DEVICE);
+	desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl,
+						1, DMA_TO_DEVICE, 1);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+	/* [3] submit the DMA */
+	set_dma_type(this, DMA_FOR_WRITE_DATA);
+	return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_read_data(struct gpmi_nand_data *this)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = get_dma_chan(this);
+	int chip = this->current_chip;
+	u32 pio[2];
+
+	/* [1] : send PIO */
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
+		| BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+	pio[1] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 1 error\n");
+		return -1;
+	}
+
+	/* [2] : send DMA request */
+	prepare_data_dma(this, DMA_FROM_DEVICE);
+	desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl,
+						1, DMA_FROM_DEVICE, 1);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+
+	/* [3] : submit the DMA */
+	set_dma_type(this, DMA_FOR_READ_DATA);
+	return start_dma_without_bch_irq(this, desc);
+}
+
+int gpmi_send_page(struct gpmi_nand_data *this,
+			dma_addr_t payload, dma_addr_t auxiliary)
+{
+	struct bch_geometry *geo = &this->bch_geometry;
+	uint32_t command_mode;
+	uint32_t address;
+	uint32_t ecc_command;
+	uint32_t buffer_mask;
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = get_dma_chan(this);
+	int chip = this->current_chip;
+	u32 pio[6];
+
+	/* 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;
+
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(0);
+	pio[1] = 0;
+	pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
+		| BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+		| BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+	pio[3] = geo->page_size_in_bytes;
+	pio[4] = payload;
+	pio[5] = auxiliary;
+
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+	set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE);
+	return start_dma_with_bch_irq(this, desc);
+}
+
+int gpmi_read_page(struct gpmi_nand_data *this,
+				dma_addr_t payload, dma_addr_t auxiliary)
+{
+	struct bch_geometry *geo = &this->bch_geometry;
+	uint32_t command_mode;
+	uint32_t address;
+	uint32_t ecc_command;
+	uint32_t buffer_mask;
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *channel = get_dma_chan(this);
+	int chip = this->current_chip;
+	u32 pio[6];
+
+	/* [1] Wait for the chip to report ready. */
+	command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+	address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+	pio[0] =  BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(0);
+	pio[1] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+				(struct scatterlist *)pio, 2, DMA_NONE, 0);
+	if (!desc) {
+		pr_err("step 1 error\n");
+		return -1;
+	}
+
+	/* [2] 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;
+
+	pio[0] =  BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes);
+
+	pio[1] = 0;
+	pio[2] =  BM_GPMI_ECCCTRL_ENABLE_ECC
+		| BF_GPMI_ECCCTRL_ECC_CMD(ecc_command)
+		| BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+	pio[3] = geo->page_size_in_bytes;
+	pio[4] = payload;
+	pio[5] = auxiliary;
+	desc = channel->device->device_prep_slave_sg(channel,
+					(struct scatterlist *)pio,
+					ARRAY_SIZE(pio), DMA_NONE, 1);
+	if (!desc) {
+		pr_err("step 2 error\n");
+		return -1;
+	}
+
+	/* [3] Disable the BCH block */
+	command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+	address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode)
+		| BM_GPMI_CTRL0_WORD_LENGTH
+		| BF_GPMI_CTRL0_CS(chip, this)
+		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
+		| BF_GPMI_CTRL0_ADDRESS(address)
+		| BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes);
+	pio[1] = 0;
+	desc = channel->device->device_prep_slave_sg(channel,
+				(struct scatterlist *)pio, 2, DMA_NONE, 1);
+	if (!desc) {
+		pr_err("step 3 error\n");
+		return -1;
+	}
+
+	/* [4] submit the DMA */
+	set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
+	return start_dma_with_bch_irq(this, desc);
+}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
new file mode 100644
index 0000000..4102050
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h
@@ -0,0 +1,168 @@
+/*
+ * Freescale GPMI NAND Flash Driver
+ *
+ * Copyright 2008-2011 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_NAND_GPMI_REGS_H
+#define __GPMI_NAND_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 BP_GPMI_CTRL0_COMMAND_MODE			24
+#define BM_GPMI_CTRL0_COMMAND_MODE	(3 << BP_GPMI_CTRL0_COMMAND_MODE)
+#define BF_GPMI_CTRL0_COMMAND_MODE(v)	\
+	(((v) << BP_GPMI_CTRL0_COMMAND_MODE) & 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			(1 << 23)
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT		0x0
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT		0x1
+
+/*
+ *  Difference in LOCK_CS between imx23 and imx28 :
+ *  This bit may impact the _POWER_ consumption. So some chips
+ *  do not set it.
+ */
+#define MX23_BP_GPMI_CTRL0_LOCK_CS			22
+#define MX28_BP_GPMI_CTRL0_LOCK_CS			27
+#define LOCK_CS_ENABLE					0x1
+#define BF_GPMI_CTRL0_LOCK_CS(v, x)			0x0
+
+/* Difference in CS between imx23 and imx28 */
+#define BP_GPMI_CTRL0_CS				20
+#define MX23_BM_GPMI_CTRL0_CS		(3 << BP_GPMI_CTRL0_CS)
+#define MX28_BM_GPMI_CTRL0_CS		(7 << BP_GPMI_CTRL0_CS)
+#define BF_GPMI_CTRL0_CS(v, x)		(((v) << BP_GPMI_CTRL0_CS) & \
+						(GPMI_IS_MX23((x)) \
+						? MX23_BM_GPMI_CTRL0_CS	\
+						: MX28_BM_GPMI_CTRL0_CS))
+
+#define BP_GPMI_CTRL0_ADDRESS				17
+#define BM_GPMI_CTRL0_ADDRESS		(3 << BP_GPMI_CTRL0_ADDRESS)
+#define BF_GPMI_CTRL0_ADDRESS(v)	\
+		(((v) << BP_GPMI_CTRL0_ADDRESS) & 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			(1 << 16)
+#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	(0xffff << BP_GPMI_CTRL0_XFER_COUNT)
+#define BF_GPMI_CTRL0_XFER_COUNT(v)	\
+		(((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT)
+
+#define HW_GPMI_COMPARE					0x00000010
+
+#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_ECC_CMD				13
+#define BM_GPMI_ECCCTRL_ECC_CMD		(3 << BP_GPMI_ECCCTRL_ECC_CMD)
+#define BF_GPMI_ECCCTRL_ECC_CMD(v)	\
+		(((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD)
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE		0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE		0x1
+
+#define BM_GPMI_ECCCTRL_ENABLE_ECC			(1 << 12)
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE		0x1
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE		0x0
+
+#define BP_GPMI_ECCCTRL_BUFFER_MASK			0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK	(0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK)
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v)	\
+	(((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & 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 HW_GPMI_PAYLOAD					0x00000040
+#define HW_GPMI_AUXILIARY				0x00000050
+#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_BCH_MODE				(1 << 18)
+
+#define BP_GPMI_CTRL1_DLL_ENABLE			17
+#define BM_GPMI_CTRL1_DLL_ENABLE	(1 << BP_GPMI_CTRL1_DLL_ENABLE)
+
+#define BP_GPMI_CTRL1_HALF_PERIOD			16
+#define BM_GPMI_CTRL1_HALF_PERIOD	(1 << BP_GPMI_CTRL1_HALF_PERIOD)
+
+#define BP_GPMI_CTRL1_RDN_DELAY				12
+#define BM_GPMI_CTRL1_RDN_DELAY		(0xf << BP_GPMI_CTRL1_RDN_DELAY)
+#define BF_GPMI_CTRL1_RDN_DELAY(v)	\
+		(((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY)
+
+#define BM_GPMI_CTRL1_DEV_RESET				(1 << 3)
+#define BV_GPMI_CTRL1_DEV_RESET__ENABLED		0x0
+#define BV_GPMI_CTRL1_DEV_RESET__DISABLED		0x1
+
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY		(1 << 2)
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW	0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH	0x1
+
+#define BM_GPMI_CTRL1_CAMERA_MODE			(1 << 1)
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND			0x0
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA			0x1
+
+#define BM_GPMI_CTRL1_GPMI_MODE				(1 << 0)
+
+#define HW_GPMI_TIMING0					0x00000070
+
+#define BP_GPMI_TIMING0_ADDRESS_SETUP			16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP	(0xff << BP_GPMI_TIMING0_ADDRESS_SETUP)
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v)	\
+	(((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+
+#define BP_GPMI_TIMING0_DATA_HOLD			8
+#define BM_GPMI_TIMING0_DATA_HOLD	(0xff << BP_GPMI_TIMING0_DATA_HOLD)
+#define BF_GPMI_TIMING0_DATA_HOLD(v)		\
+	(((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD)
+
+#define BP_GPMI_TIMING0_DATA_SETUP			0
+#define BM_GPMI_TIMING0_DATA_SETUP	(0xff << BP_GPMI_TIMING0_DATA_SETUP)
+#define BF_GPMI_TIMING0_DATA_SETUP(v)		\
+	(((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP)
+
+#define HW_GPMI_TIMING1					0x00000080
+#define HW_GPMI_TIMING2					0x00000090
+#define HW_GPMI_DATA					0x000000a0
+/*============================ MX28 uses this to detect READY ==============*/
+#define HW_GPMI_STAT					0x000000b0
+#define MX28_BP_GPMI_STAT_READY_BUSY			24
+#define MX28_BM_GPMI_STAT_READY_BUSY	(0xff << MX28_BP_GPMI_STAT_READY_BUSY)
+#define MX28_BF_GPMI_STAT_READY_BUSY(v)		\
+	(((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY)
+/*============================ MX23 uses this to detect READY ==============*/
+#define HW_GPMI_DEBUG					0x000000c0
+#define MX23_BP_GPMI_DEBUG_READY0			28
+#define MX23_BM_GPMI_DEBUG_READY0	(1 << MX23_BP_GPMI_DEBUG_READY0)
+#endif
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v10 3/3] MTD : add GPMI-NAND driver in the config and Makefile
  2011-08-24  7:33 ` Huang Shijie
@ 2011-08-24  7:33   ` Huang Shijie
  -1 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-24  7:33 UTC (permalink / raw)
  To: dedekind1
  Cc: koen.beel.barco, w.sang, Huang Shijie, linux-mtd, shijie8,
	s.hauer, linux-arm-kernel

add the GPMI-NAND driver in the relevant Kconfig and Makefile in the MTD.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/nand/Kconfig            |   13 +++++++++++++
 drivers/mtd/nand/Makefile           |    1 +
 drivers/mtd/nand/gpmi-nand/Makefile |    3 +++
 3 files changed, 17 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/gpmi-nand/Makefile

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 7ec5b49..42b7b86 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -417,6 +417,19 @@ config MTD_NAND_NANDSIM
 	  The simulator may simulate various NAND flash chips for the
 	  MTD nand layer.
 
+config MTD_NAND_GPMI_NAND
+        bool "GPMI NAND Flash Controller driver"
+        depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28)
+	select MTD_PARTITIONS
+	select MTD_CMDLINE_PARTS
+        help
+	 Enables NAND Flash support for IMX23 or IMX28.
+	 The GPMI controller is very powerful, with the help of BCH
+	 module, it can do the hardware ECC. The GPMI supports several
+	 NAND flashs at the same time. The GPMI may conflicts with other
+	 block, such as SD card. So pay attention to it when you enable
+	 the GPMI.
+
 config MTD_NAND_PLATFORM
 	tristate "Support for generic platform NAND driver"
 	help
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index c9334e9..618f4ba 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -48,5 +48,6 @@ obj-$(CONFIG_MTD_NAND_BCM_UMI)		+= bcm_umi_nand.o nand_bcm_umi.o
 obj-$(CONFIG_MTD_NAND_MPC5121_NFC)	+= mpc5121_nfc.o
 obj-$(CONFIG_MTD_NAND_RICOH)		+= r852.o
 obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
+obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/gpmi-nand/Makefile b/drivers/mtd/nand/gpmi-nand/Makefile
new file mode 100644
index 0000000..3a46248
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o
+gpmi_nand-objs += gpmi-nand.o
+gpmi_nand-objs += gpmi-lib.o
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v10 3/3] MTD : add GPMI-NAND driver in the config and Makefile
@ 2011-08-24  7:33   ` Huang Shijie
  0 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-24  7:33 UTC (permalink / raw)
  To: linux-arm-kernel

add the GPMI-NAND driver in the relevant Kconfig and Makefile in the MTD.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/nand/Kconfig            |   13 +++++++++++++
 drivers/mtd/nand/Makefile           |    1 +
 drivers/mtd/nand/gpmi-nand/Makefile |    3 +++
 3 files changed, 17 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/gpmi-nand/Makefile

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 7ec5b49..42b7b86 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -417,6 +417,19 @@ config MTD_NAND_NANDSIM
 	  The simulator may simulate various NAND flash chips for the
 	  MTD nand layer.
 
+config MTD_NAND_GPMI_NAND
+        bool "GPMI NAND Flash Controller driver"
+        depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28)
+	select MTD_PARTITIONS
+	select MTD_CMDLINE_PARTS
+        help
+	 Enables NAND Flash support for IMX23 or IMX28.
+	 The GPMI controller is very powerful, with the help of BCH
+	 module, it can do the hardware ECC. The GPMI supports several
+	 NAND flashs at the same time. The GPMI may conflicts with other
+	 block, such as SD card. So pay attention to it when you enable
+	 the GPMI.
+
 config MTD_NAND_PLATFORM
 	tristate "Support for generic platform NAND driver"
 	help
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index c9334e9..618f4ba 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -48,5 +48,6 @@ obj-$(CONFIG_MTD_NAND_BCM_UMI)		+= bcm_umi_nand.o nand_bcm_umi.o
 obj-$(CONFIG_MTD_NAND_MPC5121_NFC)	+= mpc5121_nfc.o
 obj-$(CONFIG_MTD_NAND_RICOH)		+= r852.o
 obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
+obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/gpmi-nand/Makefile b/drivers/mtd/nand/gpmi-nand/Makefile
new file mode 100644
index 0000000..3a46248
--- /dev/null
+++ b/drivers/mtd/nand/gpmi-nand/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o
+gpmi_nand-objs += gpmi-nand.o
+gpmi_nand-objs += gpmi-lib.o
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
  2011-08-24  7:33 ` Huang Shijie
@ 2011-08-25 11:34   ` Artem Bityutskiy
  -1 siblings, 0 replies; 24+ messages in thread
From: Artem Bityutskiy @ 2011-08-25 11:34 UTC (permalink / raw)
  To: Huang Shijie
  Cc: koen.beel.barco, w.sang, linux-mtd, shijie8, s.hauer, linux-arm-kernel

On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
> The patch set is based on Artem's tree:
> 	http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
> 
> The general-purpose media interface(GPMI) controller is a flexible interface
> to up to several NAND flashs.
> 
> The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
> 
> With the help of BCH, the GPMI controller can choose to do the hardware ECC or
> not.
> 
> This driver is a _pure_ MTD NAND controller driver now.

I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
independent of the other patches of yours: "[PATCH v8 0/4] add the
GPMI-NAND support for imx23/imx28".

I did not really review the driver carefully, but the code looks neat at
the first glance. My tree is in linux-next, so you should see your
driver there.

Also, if someone gives it more review, and you send a new version, I can
always pick it up.

Thanks.

-- 
Best Regards,
Artem Bityutskiy

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
@ 2011-08-25 11:34   ` Artem Bityutskiy
  0 siblings, 0 replies; 24+ messages in thread
From: Artem Bityutskiy @ 2011-08-25 11:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
> The patch set is based on Artem's tree:
> 	http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
> 
> The general-purpose media interface(GPMI) controller is a flexible interface
> to up to several NAND flashs.
> 
> The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
> 
> With the help of BCH, the GPMI controller can choose to do the hardware ECC or
> not.
> 
> This driver is a _pure_ MTD NAND controller driver now.

I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
independent of the other patches of yours: "[PATCH v8 0/4] add the
GPMI-NAND support for imx23/imx28".

I did not really review the driver carefully, but the code looks neat at
the first glance. My tree is in linux-next, so you should see your
driver there.

Also, if someone gives it more review, and you send a new version, I can
always pick it up.

Thanks.

-- 
Best Regards,
Artem Bityutskiy

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
  2011-08-25 11:34   ` Artem Bityutskiy
@ 2011-08-26  3:22     ` Huang Shijie
  -1 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-26  3:22 UTC (permalink / raw)
  To: dedekind1
  Cc: koen.beel.barco, w.sang, linux-mtd, shijie8, s.hauer, linux-arm-kernel

Hi Artem:
> On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
>> The patch set is based on Artem's tree:
>> 	http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
>>
>> The general-purpose media interface(GPMI) controller is a flexible interface
>> to up to several NAND flashs.
>>
>> The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
>>
>> With the help of BCH, the GPMI controller can choose to do the hardware ECC or
>> not.
>>
>> This driver is a _pure_ MTD NAND controller driver now.
> I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
> independent of the other patches of yours: "[PATCH v8 0/4] add the
> GPMI-NAND support for imx23/imx28".
>
thanks very much :)

Huang Shijie
> I did not really review the driver carefully, but the code looks neat at
> the first glance. My tree is in linux-next, so you should see your
> driver there.
>
> Also, if someone gives it more review, and you send a new version, I can
> always pick it up.
>
> Thanks.
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
@ 2011-08-26  3:22     ` Huang Shijie
  0 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-26  3:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Artem:
> On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
>> The patch set is based on Artem's tree:
>> 	http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
>>
>> The general-purpose media interface(GPMI) controller is a flexible interface
>> to up to several NAND flashs.
>>
>> The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC accelerator.
>>
>> With the help of BCH, the GPMI controller can choose to do the hardware ECC or
>> not.
>>
>> This driver is a _pure_ MTD NAND controller driver now.
> I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
> independent of the other patches of yours: "[PATCH v8 0/4] add the
> GPMI-NAND support for imx23/imx28".
>
thanks very much :)

Huang Shijie
> I did not really review the driver carefully, but the code looks neat at
> the first glance. My tree is in linux-next, so you should see your
> driver there.
>
> Also, if someone gives it more review, and you send a new version, I can
> always pick it up.
>
> Thanks.
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
  2011-08-26  3:22     ` Huang Shijie
@ 2011-08-26 13:34       ` Koen Beel
  -1 siblings, 0 replies; 24+ messages in thread
From: Koen Beel @ 2011-08-26 13:34 UTC (permalink / raw)
  To: Huang Shijie
  Cc: dedekind1, s.hauer, w.sang, linux-mtd, shijie8, linux-arm-kernel

Tested-by: Koen Beel <koen.beel@barco.com>

On Fri, Aug 26, 2011 at 5:22 AM, Huang Shijie <b32955@freescale.com> wrote:
> Hi Artem:
>>
>> On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
>>>
>>> The patch set is based on Artem's tree:
>>>        http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
>>>
>>> The general-purpose media interface(GPMI) controller is a flexible
>>> interface
>>> to up to several NAND flashs.
>>>
>>> The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC
>>> accelerator.
>>>
>>> With the help of BCH, the GPMI controller can choose to do the hardware
>>> ECC or
>>> not.
>>>
>>> This driver is a _pure_ MTD NAND controller driver now.
>>
>> I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
>> independent of the other patches of yours: "[PATCH v8 0/4] add the
>> GPMI-NAND support for imx23/imx28".
>>
> thanks very much :)
>
> Huang Shijie
>>
>> I did not really review the driver carefully, but the code looks neat at
>> the first glance. My tree is in linux-next, so you should see your
>> driver there.
>>
>> Also, if someone gives it more review, and you send a new version, I can
>> always pick it up.
>>
>> Thanks.
>>
>
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
@ 2011-08-26 13:34       ` Koen Beel
  0 siblings, 0 replies; 24+ messages in thread
From: Koen Beel @ 2011-08-26 13:34 UTC (permalink / raw)
  To: linux-arm-kernel

Tested-by: Koen Beel <koen.beel@barco.com>

On Fri, Aug 26, 2011 at 5:22 AM, Huang Shijie <b32955@freescale.com> wrote:
> Hi Artem:
>>
>> On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
>>>
>>> The patch set is based on Artem's tree:
>>> ? ? ? ?http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
>>>
>>> The general-purpose media interface(GPMI) controller is a flexible
>>> interface
>>> to up to several NAND flashs.
>>>
>>> The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC
>>> accelerator.
>>>
>>> With the help of BCH, the GPMI controller can choose to do the hardware
>>> ECC or
>>> not.
>>>
>>> This driver is a _pure_ MTD NAND controller driver now.
>>
>> I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
>> independent of the other patches of yours: "[PATCH v8 0/4] add the
>> GPMI-NAND support for imx23/imx28".
>>
> thanks very much :)
>
> Huang Shijie
>>
>> I did not really review the driver carefully, but the code looks neat at
>> the first glance. My tree is in linux-next, so you should see your
>> driver there.
>>
>> Also, if someone gives it more review, and you send a new version, I can
>> always pick it up.
>>
>> Thanks.
>>
>
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v10 1/3] MTD : add the common code for GPMI-NAND controller driver
  2011-08-24  7:33   ` Huang Shijie
@ 2011-08-26 15:12     ` Marek Vasut
  -1 siblings, 0 replies; 24+ messages in thread
From: Marek Vasut @ 2011-08-26 15:12 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: dedekind1, koen.beel.barco, w.sang, Huang Shijie, linux-mtd,
	shijie8, s.hauer

On Wednesday, August 24, 2011 09:33:22 AM Huang Shijie wrote:
> These files contain the common code for the GPMI-NAND driver.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>

[...]

Hi Huang, please don't forget to CC me next time, but there will be next time 
for this patch I guess ;-)

> +int start_dma_without_bch_irq(struct gpmi_nand_data *this,
> +				struct dma_async_tx_descriptor *desc)
> +{
> +	struct completion *dma_c = &this->dma_done;
> +	int err;
> +
> +	init_completion(dma_c);
> +
> +	desc->callback		= dma_irq_callback;
> +	desc->callback_param	= this;
> +	dmaengine_submit(desc);
> +
> +	/* Wait for the interrupt from the DMA block. */
> +	err = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
> +	err = (!err) ? -ETIMEDOUT : 0;

This might need a tiny correction.

> +	if (err) {
> +		pr_err("DMA timeout, last DMA :%d\n", this->last_dma_type);
> +		if (gpmi_debug & GPMI_DEBUG_VERBOSE)
> +			gpmi_dump_info(this);
> +	}
> +	return err;
> +}

But that's a minor thing. Even like this:

Acked-by: Marek Vasut <marek.vasut@gmail.com>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 1/3] MTD : add the common code for GPMI-NAND controller driver
@ 2011-08-26 15:12     ` Marek Vasut
  0 siblings, 0 replies; 24+ messages in thread
From: Marek Vasut @ 2011-08-26 15:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday, August 24, 2011 09:33:22 AM Huang Shijie wrote:
> These files contain the common code for the GPMI-NAND driver.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>

[...]

Hi Huang, please don't forget to CC me next time, but there will be next time 
for this patch I guess ;-)

> +int start_dma_without_bch_irq(struct gpmi_nand_data *this,
> +				struct dma_async_tx_descriptor *desc)
> +{
> +	struct completion *dma_c = &this->dma_done;
> +	int err;
> +
> +	init_completion(dma_c);
> +
> +	desc->callback		= dma_irq_callback;
> +	desc->callback_param	= this;
> +	dmaengine_submit(desc);
> +
> +	/* Wait for the interrupt from the DMA block. */
> +	err = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
> +	err = (!err) ? -ETIMEDOUT : 0;

This might need a tiny correction.

> +	if (err) {
> +		pr_err("DMA timeout, last DMA :%d\n", this->last_dma_type);
> +		if (gpmi_debug & GPMI_DEBUG_VERBOSE)
> +			gpmi_dump_info(this);
> +	}
> +	return err;
> +}

But that's a minor thing. Even like this:

Acked-by: Marek Vasut <marek.vasut@gmail.com>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v10 2/3] MTD : add helper functions library and header files for GPMI NAND driver
  2011-08-24  7:33   ` Huang Shijie
@ 2011-08-26 15:15     ` Marek Vasut
  -1 siblings, 0 replies; 24+ messages in thread
From: Marek Vasut @ 2011-08-26 15:15 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: dedekind1, koen.beel.barco, w.sang, Huang Shijie, linux-mtd,
	shijie8, s.hauer

On Wednesday, August 24, 2011 09:33:23 AM Huang Shijie wrote:
> bch-regs.h : registers file for BCH module
> gpmi-regs.h: registers file for GPMI module
> gpmi-lib.c: helper functions library.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>

[...]

> +/* Returns the Ready/Busy status of the given chip. */
> +int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
> +{
> +	struct resources *r = &this->resources;
> +	uint32_t mask;
> +	uint32_t reg;
> +
> +	if (GPMI_IS_MX23(this)) {
> +		mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
> +		reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
> +	} else if (GPMI_IS_MX28(this)) {
> +		mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip);
> +		reg = readl(r->gpmi_regs + HW_GPMI_STAT);
> +	} else
> +		BUG();
> +	return !!(reg & mask);

Maybe you don't need that double negation here ?

> +}

[...]

> +#define HW_GPMI_TIMING1					0x00000080
> +#define HW_GPMI_TIMING2					0x00000090
> +#define HW_GPMI_DATA					0x000000a0
> +/*============================ MX28 uses this to detect READY
> ==============*/ +#define HW_GPMI_STAT					0x000000b0
> +#define MX28_BP_GPMI_STAT_READY_BUSY			24
> +#define MX28_BM_GPMI_STAT_READY_BUSY	(0xff <<
> MX28_BP_GPMI_STAT_READY_BUSY) +#define MX28_BF_GPMI_STAT_READY_BUSY(v)		
\
> +	(((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY)
> +/*============================ MX23 uses this to detect READY
> ==============*/ +#define HW_GPMI_DEBUG					0x000000c0
> +#define MX23_BP_GPMI_DEBUG_READY0			28
> +#define MX23_BM_GPMI_DEBUG_READY0	(1 << MX23_BP_GPMI_DEBUG_READY0)
> +#endif

Maybe remove those "==========" above.

Minor things,

Acked-by: Marek Vasut <marek.vasut@gmail.com>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 2/3] MTD : add helper functions library and header files for GPMI NAND driver
@ 2011-08-26 15:15     ` Marek Vasut
  0 siblings, 0 replies; 24+ messages in thread
From: Marek Vasut @ 2011-08-26 15:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday, August 24, 2011 09:33:23 AM Huang Shijie wrote:
> bch-regs.h : registers file for BCH module
> gpmi-regs.h: registers file for GPMI module
> gpmi-lib.c: helper functions library.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>

[...]

> +/* Returns the Ready/Busy status of the given chip. */
> +int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
> +{
> +	struct resources *r = &this->resources;
> +	uint32_t mask;
> +	uint32_t reg;
> +
> +	if (GPMI_IS_MX23(this)) {
> +		mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
> +		reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
> +	} else if (GPMI_IS_MX28(this)) {
> +		mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip);
> +		reg = readl(r->gpmi_regs + HW_GPMI_STAT);
> +	} else
> +		BUG();
> +	return !!(reg & mask);

Maybe you don't need that double negation here ?

> +}

[...]

> +#define HW_GPMI_TIMING1					0x00000080
> +#define HW_GPMI_TIMING2					0x00000090
> +#define HW_GPMI_DATA					0x000000a0
> +/*============================ MX28 uses this to detect READY
> ==============*/ +#define HW_GPMI_STAT					0x000000b0
> +#define MX28_BP_GPMI_STAT_READY_BUSY			24
> +#define MX28_BM_GPMI_STAT_READY_BUSY	(0xff <<
> MX28_BP_GPMI_STAT_READY_BUSY) +#define MX28_BF_GPMI_STAT_READY_BUSY(v)		
\
> +	(((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY)
> +/*============================ MX23 uses this to detect READY
> ==============*/ +#define HW_GPMI_DEBUG					0x000000c0
> +#define MX23_BP_GPMI_DEBUG_READY0			28
> +#define MX23_BM_GPMI_DEBUG_READY0	(1 << MX23_BP_GPMI_DEBUG_READY0)
> +#endif

Maybe remove those "==========" above.

Minor things,

Acked-by: Marek Vasut <marek.vasut@gmail.com>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v10 3/3] MTD : add GPMI-NAND driver in the config and Makefile
  2011-08-24  7:33   ` Huang Shijie
@ 2011-08-26 15:15     ` Marek Vasut
  -1 siblings, 0 replies; 24+ messages in thread
From: Marek Vasut @ 2011-08-26 15:15 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: dedekind1, koen.beel.barco, w.sang, Huang Shijie, linux-mtd,
	shijie8, s.hauer

On Wednesday, August 24, 2011 09:33:24 AM Huang Shijie wrote:
> add the GPMI-NAND driver in the relevant Kconfig and Makefile in the MTD.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>

[...]

Acked-by: Marek Vasut <marek.vasut@gmail.com>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 3/3] MTD : add GPMI-NAND driver in the config and Makefile
@ 2011-08-26 15:15     ` Marek Vasut
  0 siblings, 0 replies; 24+ messages in thread
From: Marek Vasut @ 2011-08-26 15:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday, August 24, 2011 09:33:24 AM Huang Shijie wrote:
> add the GPMI-NAND driver in the relevant Kconfig and Makefile in the MTD.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>

[...]

Acked-by: Marek Vasut <marek.vasut@gmail.com>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
  2011-08-25 11:34   ` Artem Bityutskiy
@ 2011-08-26 15:15     ` Marek Vasut
  -1 siblings, 0 replies; 24+ messages in thread
From: Marek Vasut @ 2011-08-26 15:15 UTC (permalink / raw)
  To: linux-arm-kernel, dedekind1
  Cc: koen.beel.barco, w.sang, Huang Shijie, linux-mtd, shijie8, s.hauer

On Thursday, August 25, 2011 01:34:46 PM Artem Bityutskiy wrote:
> On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
> > The patch set is based on Artem's tree:
> > 	http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
> > 
> > The general-purpose media interface(GPMI) controller is a flexible
> > interface to up to several NAND flashs.
> > 
> > The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC
> > accelerator.
> > 
> > With the help of BCH, the GPMI controller can choose to do the hardware
> > ECC or not.
> > 
> > This driver is a _pure_ MTD NAND controller driver now.
> 
> I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
> independent of the other patches of yours: "[PATCH v8 0/4] add the
> GPMI-NAND support for imx23/imx28".
> 
> I did not really review the driver carefully, but the code looks neat at
> the first glance. My tree is in linux-next, so you should see your
> driver there.
> 
> Also, if someone gives it more review, and you send a new version, I can
> always pick it up.

Yea, add my Acked-by's to that version ;-)

Cheers
> 
> Thanks.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
@ 2011-08-26 15:15     ` Marek Vasut
  0 siblings, 0 replies; 24+ messages in thread
From: Marek Vasut @ 2011-08-26 15:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday, August 25, 2011 01:34:46 PM Artem Bityutskiy wrote:
> On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
> > The patch set is based on Artem's tree:
> > 	http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
> > 
> > The general-purpose media interface(GPMI) controller is a flexible
> > interface to up to several NAND flashs.
> > 
> > The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC
> > accelerator.
> > 
> > With the help of BCH, the GPMI controller can choose to do the hardware
> > ECC or not.
> > 
> > This driver is a _pure_ MTD NAND controller driver now.
> 
> I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
> independent of the other patches of yours: "[PATCH v8 0/4] add the
> GPMI-NAND support for imx23/imx28".
> 
> I did not really review the driver carefully, but the code looks neat at
> the first glance. My tree is in linux-next, so you should see your
> driver there.
> 
> Also, if someone gives it more review, and you send a new version, I can
> always pick it up.

Yea, add my Acked-by's to that version ;-)

Cheers
> 
> Thanks.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
  2011-08-26 15:15     ` Marek Vasut
@ 2011-08-27  1:03       ` Huang Shijie
  -1 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-27  1:03 UTC (permalink / raw)
  To: Marek Vasut
  Cc: dedekind1, koen.beel.barco, w.sang, Huang Shijie, linux-mtd,
	s.hauer, linux-arm-kernel

Hi Marek:

On Fri, Aug 26, 2011 at 11:15 AM, Marek Vasut <marek.vasut@gmail.com> wrote:
> On Thursday, August 25, 2011 01:34:46 PM Artem Bityutskiy wrote:
>> On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
>> > The patch set is based on Artem's tree:
>> >     http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
>> >
>> > The general-purpose media interface(GPMI) controller is a flexible
>> > interface to up to several NAND flashs.
>> >
>> > The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC
>> > accelerator.
>> >
>> > With the help of BCH, the GPMI controller can choose to do the hardware
>> > ECC or not.
>> >
>> > This driver is a _pure_ MTD NAND controller driver now.
>>
>> I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
>> independent of the other patches of yours: "[PATCH v8 0/4] add the
>> GPMI-NAND support for imx23/imx28".
>>
>> I did not really review the driver carefully, but the code looks neat at
>> the first glance. My tree is in linux-next, so you should see your
>> driver there.
>>
>> Also, if someone gives it more review, and you send a new version, I can
>> always pick it up.
>
> Yea, add my Acked-by's to that version ;-)
Thanks for your comments. :)

I will send out the new version next week.

Best Regards
Huang Shijie

>
> Cheers
>>
>> Thanks.
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28
@ 2011-08-27  1:03       ` Huang Shijie
  0 siblings, 0 replies; 24+ messages in thread
From: Huang Shijie @ 2011-08-27  1:03 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marek:

On Fri, Aug 26, 2011 at 11:15 AM, Marek Vasut <marek.vasut@gmail.com> wrote:
> On Thursday, August 25, 2011 01:34:46 PM Artem Bityutskiy wrote:
>> On Wed, 2011-08-24 at 15:33 +0800, Huang Shijie wrote:
>> > The patch set is based on Artem's tree:
>> > ? ? http://git.infradead.org/users/dedekind/l2-mtd-2.6.git
>> >
>> > The general-purpose media interface(GPMI) controller is a flexible
>> > interface to up to several NAND flashs.
>> >
>> > The Bose Ray-Choudhury Hocquenghem(BCH) module is a hardware ECC
>> > accelerator.
>> >
>> > With the help of BCH, the GPMI controller can choose to do the hardware
>> > ECC or not.
>> >
>> > This driver is a _pure_ MTD NAND controller driver now.
>>
>> I've pushed these patches to my l2-mtd-2.6.git tree. I assume they are
>> independent of the other patches of yours: "[PATCH v8 0/4] add the
>> GPMI-NAND support for imx23/imx28".
>>
>> I did not really review the driver carefully, but the code looks neat at
>> the first glance. My tree is in linux-next, so you should see your
>> driver there.
>>
>> Also, if someone gives it more review, and you send a new version, I can
>> always pick it up.
>
> Yea, add my Acked-by's to that version ;-)
Thanks for your comments. :)

I will send out the new version next week.

Best Regards
Huang Shijie

>
> Cheers
>>
>> Thanks.
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2011-08-27  1:03 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-24  7:33 [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28 Huang Shijie
2011-08-24  7:33 ` Huang Shijie
2011-08-24  7:33 ` [PATCH v10 1/3] MTD : add the common code for GPMI-NAND controller driver Huang Shijie
2011-08-24  7:33   ` Huang Shijie
2011-08-26 15:12   ` Marek Vasut
2011-08-26 15:12     ` Marek Vasut
2011-08-24  7:33 ` [PATCH v10 2/3] MTD : add helper functions library and header files for GPMI NAND driver Huang Shijie
2011-08-24  7:33   ` Huang Shijie
2011-08-26 15:15   ` Marek Vasut
2011-08-26 15:15     ` Marek Vasut
2011-08-24  7:33 ` [PATCH v10 3/3] MTD : add GPMI-NAND driver in the config and Makefile Huang Shijie
2011-08-24  7:33   ` Huang Shijie
2011-08-26 15:15   ` Marek Vasut
2011-08-26 15:15     ` Marek Vasut
2011-08-25 11:34 ` [PATCH v10 0/3] add the GPMI controller driver for IMX23/IMX28 Artem Bityutskiy
2011-08-25 11:34   ` Artem Bityutskiy
2011-08-26  3:22   ` Huang Shijie
2011-08-26  3:22     ` Huang Shijie
2011-08-26 13:34     ` Koen Beel
2011-08-26 13:34       ` Koen Beel
2011-08-26 15:15   ` Marek Vasut
2011-08-26 15:15     ` Marek Vasut
2011-08-27  1:03     ` Huang Shijie
2011-08-27  1:03       ` Huang Shijie

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.