diff mbox

[1/3] mtd: spi-nand framework

Message ID 87F60714EC601C4C83DFF1D2E3D390A04AB404@NTXXIAMBX02.xacn.micron.com
State Rejected
Headers show

Commit Message

Peter Pan 潘栋 (peterpandong) Jan. 8, 2015, 12:48 a.m. UTC
Add framework to support spi nand devices. The code is derived from
parallel nand code.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 drivers/mtd/Kconfig                  |    2 +
 drivers/mtd/Makefile                 |    1 +
 drivers/mtd/spi-nand/Kconfig         |    7 +
 drivers/mtd/spi-nand/Makefile        |    2 +
 drivers/mtd/spi-nand/spi-nand-base.c | 2034 ++++++++++++++++++++++++++++++++++
 drivers/mtd/spi-nand/spi-nand-bbt.c  | 1279 +++++++++++++++++++++
 include/linux/mtd/spi-nand.h         |  317 ++++++
 7 files changed, 3642 insertions(+)
 create mode 100644 drivers/mtd/spi-nand/Kconfig
 create mode 100644 drivers/mtd/spi-nand/Makefile
 create mode 100644 drivers/mtd/spi-nand/spi-nand-base.c
 create mode 100644 drivers/mtd/spi-nand/spi-nand-bbt.c
 create mode 100644 include/linux/mtd/spi-nand.h

Comments

Ezequiel Garcia Jan. 8, 2015, 3:14 a.m. UTC | #1
On 01/07/2015 09:48 PM, Peter Pan 潘栋 (peterpandong) wrote:
[..]
> +
> +static void spi_nand_set_defaults(struct spi_nand_chip *chip)
> +{
> +	struct spi_device *spi = chip->spi;
> +
> +	if (spi->mode & SPI_RX_QUAD)
> +		chip->read_cache = spi_nand_read_from_cache_x4;
> +	else if (spi->mode & SPI_RX_DUAL)
> +		chip->read_cache = spi_nand_read_from_cache_x2;
> +	else
> +		chip->read_cache = spi_nand_read_from_cache;
> +
> +	if (!chip->reset)
> +		chip->reset = spi_nand_reset;
> +	if (!chip->erase_block)
> +		chip->erase_block = spi_nand_erase_block;
> +	if (!chip->load_page)
> +		chip->load_page = spi_nand_read_page_to_cache;
> +	if (!chip->store_cache)
> +		chip->store_cache = spi_nand_program_data_to_cache;

IMO, this is a mistake and one of the reasons why this whole file is so big.

Do you have any reason for keeping the SPI commands (read page to cache,
program data to cache, etc.) in the spi-nand-base.c ?

This shouldn't belong to the framework, but to some spi-nand-device.c
implementing the SPI NAND commands for the devices we have seen so far.
In fact, I don't think you should inherit this "default" hook stuff from
NAND, it only makes the code more obscure.

As Brian noted, such a separation would be benefitial to support a
potential SPI-NAND-specific controller.

However, I think the stronger reason is that it results in a much
simpler and clean initial code.
diff mbox

Patch

diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 71fea89..444b695 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -323,6 +323,8 @@  source "drivers/mtd/lpddr/Kconfig"
 
 source "drivers/mtd/spi-nor/Kconfig"
 
+source "drivers/mtd/spi-nand/Kconfig"
+
 source "drivers/mtd/ubi/Kconfig"
 
 endif # MTD
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 99bb9a1..581688f 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -33,4 +33,5 @@  inftl-objs		:= inftlcore.o inftlmount.o
 obj-y		+= chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
 
 obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor/
+obj-$(CONFIG_MTD_SPI_NAND)	+= spi-nand/
 obj-$(CONFIG_MTD_UBI)		+= ubi/
diff --git a/drivers/mtd/spi-nand/Kconfig b/drivers/mtd/spi-nand/Kconfig
new file mode 100644
index 0000000..b4da8f5
--- /dev/null
+++ b/drivers/mtd/spi-nand/Kconfig
@@ -0,0 +1,7 @@ 
+menuconfig MTD_SPI_NAND
+	tristate "SPI-NAND device Support"
+	depends on MTD_NAND && SPI
+	help
+	  This is the framework for the SPI NAND which can be used by the SPI
+	  device drivers and the SPI-NAND device drivers.
+
diff --git a/drivers/mtd/spi-nand/Makefile b/drivers/mtd/spi-nand/Makefile
new file mode 100644
index 0000000..6df6a34
--- /dev/null
+++ b/drivers/mtd/spi-nand/Makefile
@@ -0,0 +1,2 @@ 
+obj-$(CONFIG_MTD_SPI_NAND) += spi-nand-base.o
+obj-$(CONFIG_MTD_SPI_NAND) += spi-nand-bbt.o
diff --git a/drivers/mtd/spi-nand/spi-nand-base.c b/drivers/mtd/spi-nand/spi-nand-base.c
new file mode 100644
index 0000000..4ef5914
--- /dev/null
+++ b/drivers/mtd/spi-nand/spi-nand-base.c
@@ -0,0 +1,2034 @@ 
+/**
+* spi-nand-base.c
+*
+* Copyright (c) 2009-2014 Micron Technology, Inc.
+*
+* Derived from nand_base.c
+*
+* 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nand.h>
+#include <linux/mtd/bbm.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+
+
+static struct spi_nand_flash spi_nand_table[] = {
+	SPI_NAND_INFO("MT29F2G01AAAED", 0x2C, 0X22, 2048, 64, 64, 2048,
+			SPINAND_NEED_PLANE_SELECT),
+	SPI_NAND_INFO("MT29F4G01AAADD", 0x2C, 0X32, 2048, 64, 64, 4096,
+			SPINAND_NEED_PLANE_SELECT),
+	SPI_NAND_INFO("GD5F 512MiB 1.8V", 0xC8, 0XA4, 4096, 256, 64, 2048,
+			0),
+	SPI_NAND_INFO("GD5F 512MiB 3.3V", 0xC8, 0XB4, 4096, 256, 64, 2048,
+			0),
+	SPI_NAND_INFO("GD5F 256MiB 3.3V", 0xC8, 0XB2, 2048, 128, 64, 2048,
+			0),
+	SPI_NAND_INFO("GD5F 128MiB 3.3V", 0xC8, 0XB1, 2048, 128, 64, 1024,
+			0),
+	{.name = NULL},
+};
+
+static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo);
+
+/**
+ * spi_nand_get_device - [GENERIC] Get chip for selected access
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
+ *
+ * Get the device and lock it for exclusive access
+ */
+static int spi_nand_get_device(struct mtd_info *mtd, int new_state)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE(wait, current);
+
+	/*
+	 * Grab the lock and see if the device is available
+	 */
+	while (1) {
+		spin_lock(&this->chip_lock);
+		if (this->state == FL_READY) {
+			this->state = new_state;
+			spin_unlock(&this->chip_lock);
+			break;
+		}
+		if (new_state == FL_PM_SUSPENDED) {
+			spin_unlock(&this->chip_lock);
+			return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		add_wait_queue(&this->wq, &wait);
+		spin_unlock(&this->chip_lock);
+		schedule();
+		remove_wait_queue(&this->wq, &wait);
+	}
+	return 0;
+}
+
+/**
+ * spi_nand_release_device - [GENERIC] release chip
+ * @mtd: MTD device structure
+ *
+ * Deselect, release chip lock and wake up anyone waiting on the device
+ */
+static void spi_nand_release_device(struct mtd_info *mtd)
+{
+	struct spi_nand_chip *this = mtd->priv;
+
+	/* Release the chip */
+	spin_lock(&this->chip_lock);
+	this->state = FL_READY;
+	wake_up(&this->wq);
+	spin_unlock(&this->chip_lock);
+}
+
+/**
+ * __spi_nand_do_read_page - [INTERN] read data from flash to buffer
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @column :column address
+ * @raw: without ecc or not
+ * @corrected: how many bit error corrected
+ *
+ * read a page to buffer pointed by chip->buf
+ */
+static int __spi_nand_do_read_page(struct mtd_info *mtd, u32 page_addr,
+				u32 colunm, bool raw, int *corrected)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	int ret, ecc_error;
+	u8 status;
+
+	/*read data from chip*/
+	memset(chip->buf, 0, chip->page_size + chip->page_spare_size);
+	if (raw) {
+		ret = chip->disable_ecc(chip);
+		if (ret < 0) {
+			pr_debug("disable ecc failed\n");
+			return ret;
+		}
+	}
+	ret = chip->load_page(chip, page_addr);
+	if (ret < 0) {
+		pr_debug("error %d loading page 0x%x to cache\n",
+		ret, page_addr);
+		return ret;
+	}
+	ret = chip->waitfunc(chip, &status);
+	if (ret < 0) {
+		pr_debug("error %d waiting page 0x%x to cache\n",
+			ret, page_addr);
+		return ret;
+	}
+	chip->get_ecc_status(status, corrected, &ecc_error);
+	/*
+	 * If there's an ECC error, print a message and notify MTD
+	 * about it. Then complete the read, to load actual data on
+	 * the buffer (instead of the status result).
+	 */
+	if (ecc_error) {
+		pr_debug("internal ECC error reading page 0x%x\n",
+			page_addr);
+		mtd->ecc_stats.failed++;
+	} else if (*corrected) {
+		mtd->ecc_stats.corrected += *corrected;
+	}
+	/* Get page from the device cache into our internal buffer */
+	ret = chip->read_cache(chip, page_addr, colunm,
+			chip->page_size + chip->page_spare_size - colunm,
+			chip->buf + colunm);
+	if (ret < 0) {
+		pr_debug("error %d reading page 0x%x from cache\n",
+			ret, page_addr);
+		return ret;
+	}
+	if (raw) {
+		ret = chip->enable_ecc(chip);
+		if (ret < 0) {
+			pr_debug("enable ecc failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * spi_nand_do_read_page - [INTERN] read a page from flash to buffer
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @raw: without ecc or not
+ * @corrected: how many bit error corrected
+ *
+ * read a page to buffer pointed by chip->buf
+ */
+static int spi_nand_do_read_page(struct mtd_info *mtd, u32 page_addr,
+				bool raw, int *corrected)
+{
+	return __spi_nand_do_read_page(mtd, page_addr, 0, raw, corrected);
+}
+
+/**
+ * spi_nand_do_read_page_oob - [INTERN] read page oob from flash to buffer
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @raw: without ecc or not
+ * @corrected: how many bit error corrected
+ *
+ * read page oob to buffer pointed by chip->oobbuf
+ */
+static int spi_nand_do_read_page_oob(struct mtd_info *mtd, u32 page_addr,
+				bool raw, int *corrected)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+
+	return __spi_nand_do_read_page(mtd, page_addr, chip->page_size,
+					raw, corrected);
+}
+
+
+/**
+ * __spi_nand_do_write_page - [INTERN] write data from buffer to flash
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @column :column address
+ * @raw: without ecc or not
+ *
+ * write data from buffer pointed by chip->buf to flash
+ */
+static int __spi_nand_do_write_page(struct mtd_info *mtd, u32 page_addr,
+				u32 column, bool raw)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	u8 status;
+	bool p_fail = false;
+	int ret = 0;
+
+	if (raw) {
+		ret = chip->disable_ecc(chip);
+		if (ret < 0) {
+			pr_debug("disable ecc failed\n");
+			return ret;
+		}
+	}
+	ret = chip->write_enable(chip);
+	if (ret < 0) {
+		pr_debug("write enable command failed\n");
+		return ret;
+	}
+	/* Store the page to cache */
+	ret = chip->store_cache(chip, page_addr, column,
+			chip->page_size + chip->page_spare_size - column,
+			chip->buf + column);
+	if (ret < 0) {
+		pr_debug("error %d storing page 0x%x to cache\n",
+			ret, page_addr);
+		return ret;
+	}
+	/* Get page from the device cache into our internal buffer */
+	ret = chip->write_page(chip, page_addr);
+	if (ret < 0) {
+		pr_debug("error %d reading page 0x%x from cache\n",
+			ret, page_addr);
+		return ret;
+	}
+	ret = chip->waitfunc(chip, &status);
+	if (ret < 0) {
+		pr_debug("error %d reading page 0x%x from cache\n",
+			ret, page_addr);
+		return ret;
+	}
+	if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
+		pr_debug("program page 0x%x failed\n", page_addr);
+		p_fail = true;
+	}
+	if (raw) {
+		ret = chip->enable_ecc(chip);
+		if (ret < 0) {
+			pr_debug("enable ecc failed\n");
+			return ret;
+		}
+	}
+	if (p_fail)
+		ret = -EIO;
+
+	return ret;
+}
+
+/**
+ * spi_nand_do_write_page - [INTERN] write page from buffer to flash
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @raw: without ecc or not
+ *
+ * write page from buffer pointed by chip->buf to flash
+ */
+static int spi_nand_do_write_page(struct mtd_info *mtd, u32 page_addr,
+				bool raw)
+{
+	return __spi_nand_do_write_page(mtd, page_addr, 0, raw);
+}
+
+/**
+ * spi_nand_do_write_page_oob - [INTERN] write oob from buffer to flash
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @raw: without ecc or not
+ *
+ * write oob from buffer pointed by chip->oobbuf to flash
+ */
+static int spi_nand_do_write_page_oob(struct mtd_info *mtd, u32 page_addr,
+				bool raw)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+
+	return __spi_nand_do_write_page(mtd, page_addr, chip->page_size, raw);
+}
+
+
+/**
+ * spi_nand_transfer_oob - [INTERN] Transfer oob to client buffer
+ * @chip: SPI-NAND device structure
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
+ */
+static void spi_nand_transfer_oob(struct spi_nand_chip *chip, u8 *oob,
+				  struct mtd_oob_ops *ops, size_t len)
+{
+	switch (ops->mode) {
+
+	case MTD_OPS_PLACE_OOB:
+	case MTD_OPS_RAW:
+		memcpy(oob, chip->oobbuf + ops->ooboffs, len);
+		return;
+
+	case MTD_OPS_AUTO_OOB: {
+		struct nand_oobfree *free = chip->ecclayout->oobfree;
+		uint32_t boffs = 0, roffs = ops->ooboffs;
+		size_t bytes = 0;
+
+		for (; free->length && len; free++, len -= bytes) {
+			/* Read request not from offset 0? */
+			if (unlikely(roffs)) {
+				if (roffs >= free->length) {
+					roffs -= free->length;
+					continue;
+				}
+				boffs = free->offset + roffs;
+				bytes = min_t(size_t, len,
+					      (free->length - roffs));
+				roffs = 0;
+			} else {
+				bytes = min_t(size_t, len, free->length);
+				boffs = free->offset;
+			}
+			memcpy(oob, chip->oobbuf + boffs, bytes);
+			oob += bytes;
+		}
+		return;
+	}
+	default:
+		BUG();
+	}
+}
+
+/**
+ * spi_nand_fill_oob - [INTERN] Transfer client buffer to oob
+ * @chip: SPI-NAND device structure
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
+ */
+static void spi_nand_fill_oob(struct spi_nand_chip *chip, uint8_t *oob,
+				size_t len, struct mtd_oob_ops *ops)
+{
+	memset(chip->oobbuf, 0xff, chip->page_spare_size);
+
+	switch (ops->mode) {
+
+	case MTD_OPS_PLACE_OOB:
+	case MTD_OPS_RAW:
+		memcpy(chip->oobbuf + ops->ooboffs, oob, len);
+		return;
+
+	case MTD_OPS_AUTO_OOB: {
+		struct nand_oobfree *free = chip->ecclayout->oobfree;
+		uint32_t boffs = 0, woffs = ops->ooboffs;
+		size_t bytes = 0;
+
+		for (; free->length && len; free++, len -= bytes) {
+			/* Write request not from offset 0? */
+			if (unlikely(woffs)) {
+				if (woffs >= free->length) {
+					woffs -= free->length;
+					continue;
+				}
+				boffs = free->offset + woffs;
+				bytes = min_t(size_t, len,
+					      (free->length - woffs));
+				woffs = 0;
+			} else {
+				bytes = min_t(size_t, len, free->length);
+				boffs = free->offset;
+			}
+			memcpy(chip->oobbuf + boffs, oob, bytes);
+			oob += bytes;
+		}
+		return;
+	}
+	default:
+		BUG();
+	}
+}
+
+/**
+ * spi_nand_do_read_ops - [INTERN] Read data with ECC
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob ops structure
+ *
+ * Internal function. Called with chip held.
+ */
+static int spi_nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+			  struct mtd_oob_ops *ops)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	int page_addr, page_offset, size;
+	int ret;
+	unsigned int corrected = 0;
+	struct mtd_ecc_stats stats;
+	unsigned int max_bitflips = 0;
+	int readlen = ops->len;
+	int oobreadlen = ops->ooblen;
+	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+		mtd->oobavail : mtd->oobsize;
+
+	/* Do not allow reads past end of device */
+	if (unlikely(from >= mtd->size)) {
+		pr_debug("%s: attempt to read beyond end of device\n",
+				__func__);
+		return -EINVAL;
+	}
+	stats = mtd->ecc_stats;
+
+	page_addr = from >> chip->page_shift;
+
+	/* for main data */
+	page_offset = from & chip->page_mask;
+	ops->retlen = 0;
+
+	/* for oob */
+	if (oobreadlen > 0) {
+		if (unlikely(ops->ooboffs >= ooblen)) {
+			pr_debug("%s: attempt to start read outside oob\n",
+					__func__);
+			return -EINVAL;
+		}
+
+		if (unlikely(ops->ooboffs + oobreadlen >
+		((mtd->size >> chip->page_shift) - (from >> chip->page_shift))
+		* ooblen)) {
+			pr_debug("%s: attempt to read beyond end of device\n",
+					__func__);
+			return -EINVAL;
+		}
+		ooblen -= ops->ooboffs;
+		ops->oobretlen = 0;
+	}
+
+	while (1) {
+		if (page_addr != chip->pagebuf || oobreadlen > 0) {
+			ret = spi_nand_do_read_page(mtd, page_addr,
+					ops->mode == MTD_OPS_RAW, &corrected);
+			if (ret) {
+				pr_debug("error %d reading page 0x%x\n",
+					ret, page_addr);
+				return ret;
+			}
+			chip->pagebuf_bitflips = corrected;
+			chip->pagebuf = page_addr;
+		}
+		max_bitflips = max(max_bitflips, chip->pagebuf_bitflips);
+		size = min(readlen, chip->page_size - page_offset);
+		memcpy(ops->datbuf + ops->retlen,
+			chip->buf + page_offset, size);
+
+		ops->retlen += size;
+		readlen -= size;
+		page_offset = 0;
+
+		if (unlikely(ops->oobbuf)) {
+			size = min(oobreadlen, ooblen);
+			spi_nand_transfer_oob(chip,
+				ops->oobbuf + ops->oobretlen, ops, size);
+
+			ops->oobretlen += size;
+			oobreadlen -= size;
+		}
+		if (!readlen)
+			break;
+
+		page_addr++;
+	}
+
+	if (mtd->ecc_stats.failed - stats.failed)
+		return -EBADMSG;
+
+	return max_bitflips;
+}
+
+/**
+ * spi_nand_do_write_ops - [INTERN] SPI-NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ *
+ */
+static int spi_nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+			 struct mtd_oob_ops *ops)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	int page_addr, page_offset, size;
+	int writelen = ops->len;
+	int oobwritelen = ops->ooblen;
+	int ret;
+	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+		mtd->oobavail : mtd->oobsize;
+
+	/* Do not allow reads past end of device */
+	if (unlikely(to >= mtd->size)) {
+		pr_debug("%s: attempt to write beyond end of device\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	page_addr = to >> chip->page_shift;
+
+	/* for main data */
+	page_offset = to & chip->page_mask;
+	ops->retlen = 0;
+
+	/* for oob */
+	if (oobwritelen > 0) {
+		/* Do not allow write past end of page */
+		if ((ops->ooboffs + oobwritelen) > ooblen) {
+			pr_debug("%s: attempt to write past end of page\n",
+					__func__);
+			return -EINVAL;
+		}
+
+		if (unlikely(ops->ooboffs >= ooblen)) {
+			pr_debug("%s: attempt to start write outside oob\n",
+					__func__);
+			return -EINVAL;
+		}
+
+		if (unlikely(ops->ooboffs + oobwritelen >
+		((mtd->size >> chip->page_shift) - (to >> chip->page_shift))
+			* ooblen)) {
+			pr_debug("%s: attempt to write beyond end of device\n",
+					__func__);
+			return -EINVAL;
+		}
+		ooblen -= ops->ooboffs;
+		ops->oobretlen = 0;
+	}
+
+	chip->pagebuf = -1;
+
+	while (1) {
+		memset(chip->buf, 0xFF,
+			chip->page_size + chip->page_spare_size);
+
+		size = min(writelen, chip->page_size - page_offset);
+		memcpy(chip->buf + page_offset,
+			ops->datbuf + ops->retlen, size);
+
+		ops->retlen += size;
+		writelen -= size;
+		page_offset = 0;
+
+		if (unlikely(ops->oobbuf)) {
+			size = min(oobwritelen, ooblen);
+
+			spi_nand_fill_oob(chip, ops->oobbuf + ops->oobretlen,
+					size, ops);
+
+			ops->oobretlen += size;
+			oobwritelen -= size;
+		}
+		ret = spi_nand_do_write_page(mtd, page_addr,
+				ops->mode == MTD_OPS_RAW);
+		if (ret) {
+			pr_debug("error %d writing page 0x%x\n",
+				ret, page_addr);
+			return ret;
+		}
+		if (!writelen)
+			break;
+		page_addr++;
+	}
+	return 0;
+}
+
+/**
+ * nand_read - [MTD Interface] SPI-NAND read
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @len: number of bytes to read
+ * @retlen: pointer to variable to store the number of read bytes
+ * @buf: the databuffer to put data
+ *
+ */
+static int spi_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+	size_t *retlen, u_char *buf)
+{
+	struct mtd_oob_ops ops = {0};
+	int ret;
+
+	spi_nand_get_device(mtd, FL_READING);
+
+	ops.len = len;
+	ops.datbuf = buf;
+
+
+	ret = spi_nand_do_read_ops(mtd, from, &ops);
+
+	*retlen = ops.retlen;
+
+	spi_nand_release_device(mtd);
+
+	return ret;
+}
+
+/**
+ * spi_nand_write - [MTD Interface] SPI-NAND write
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
+ *
+ */
+static int spi_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+	size_t *retlen, const u_char *buf)
+{
+	struct mtd_oob_ops ops = {0};
+	int ret;
+
+	spi_nand_get_device(mtd, FL_WRITING);
+
+	ops.len = len;
+	ops.datbuf = (uint8_t *)buf;
+
+
+	ret =  spi_nand_do_write_ops(mtd, to, &ops);
+
+	*retlen = ops.retlen;
+
+	spi_nand_release_device(mtd);
+
+	return ret;
+}
+
+/**
+ * spi_nand_do_read_oob - [INTERN] SPI-NAND read out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ *
+ * SPI-NAND read out-of-band data from the spare area.
+ */
+static int spi_nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+			  struct mtd_oob_ops *ops)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	int page_addr;
+	int corrected = 0;
+	struct mtd_ecc_stats stats;
+	int readlen = ops->ooblen;
+	int len;
+	int ret = 0;
+
+	pr_debug("%s: from = 0x%08Lx, len = %i\n",
+			__func__, (unsigned long long)from, readlen);
+
+	stats = mtd->ecc_stats;
+
+	len = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
+
+	if (unlikely(ops->ooboffs >= len)) {
+		pr_debug("%s: attempt to start read outside oob\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	/* Do not allow reads past end of device */
+	if (unlikely(from >= mtd->size ||
+		     ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
+					(from >> chip->page_shift)) * len)) {
+		pr_debug("%s: attempt to read beyond end of device\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	/* Shift to get page */
+	page_addr = (from >> chip->page_shift);
+	len -= ops->ooboffs;
+	ops->oobretlen = 0;
+
+	while (1) {
+		/*read data from chip*/
+		ret = spi_nand_do_read_page_oob(mtd, page_addr,
+				ops->mode == MTD_OPS_RAW, &corrected);
+		if (ret) {
+			pr_debug("error %d reading page 0x%x\n",
+				ret, page_addr);
+			return ret;
+		}
+		if (page_addr == chip->pagebuf)
+			chip->pagebuf = -1;
+
+		len = min(len, readlen);
+		spi_nand_transfer_oob(chip, ops->oobbuf + ops->oobretlen,
+					ops, len);
+
+		readlen -= len;
+		ops->oobretlen += len;
+		if (!readlen)
+			break;
+
+		page_addr++;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	if (mtd->ecc_stats.failed - stats.failed)
+		return -EBADMSG;
+
+	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
+/**
+ * spi_nand_do_write_oob - [MTD Interface] SPI-NAND write out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ *
+ * SPI-NAND write out-of-band.
+ */
+static int spi_nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops)
+{
+	int page_addr, len, ret;
+	struct spi_nand_chip *chip = mtd->priv;
+	int writelen = ops->ooblen;
+
+	pr_debug("%s: to = 0x%08x, len = %i\n",
+			 __func__, (unsigned int)to, (int)writelen);
+
+	len = ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
+
+	/* Do not allow write past end of page */
+	if ((ops->ooboffs + writelen) > len) {
+		pr_debug("%s: attempt to write past end of page\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if (unlikely(ops->ooboffs >= len)) {
+		pr_debug("%s: attempt to start write outside oob\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	/* Do not allow write past end of device */
+	if (unlikely(to >= mtd->size ||
+		     ops->ooboffs + writelen >
+			((mtd->size >> chip->page_shift) -
+			 (to >> chip->page_shift)) * len)) {
+		pr_debug("%s: attempt to write beyond end of device\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	/* Shift to get page */
+	page_addr = to >> chip->page_shift;
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page_addr == chip->pagebuf)
+		chip->pagebuf = -1;
+
+	spi_nand_fill_oob(chip, ops->oobbuf, writelen, ops);
+
+	ret = spi_nand_do_write_page_oob(mtd, page_addr,
+				ops->mode == MTD_OPS_RAW);
+	if (ret) {
+		pr_debug("error %d writing page 0x%x\n",
+			ret, page_addr);
+		return ret;
+	}
+	ops->oobretlen = writelen;
+
+	return 0;
+}
+
+/**
+ * spi_nand_read_oob - [MTD Interface] SPI-NAND read data and/or out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
+ *
+ * SPI-NAND read data and/or out-of-band data.
+ */
+static int spi_nand_read_oob(struct mtd_info *mtd, loff_t from,
+			struct mtd_oob_ops *ops)
+{
+	int ret = -ENOTSUPP;
+
+	ops->retlen = 0;
+
+	/* Do not allow reads past end of device */
+	if (ops->datbuf && (from + ops->len) > mtd->size) {
+		pr_debug("%s: attempt to read beyond end of device\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	spi_nand_get_device(mtd, FL_READING);
+
+	switch (ops->mode) {
+	case MTD_OPS_PLACE_OOB:
+	case MTD_OPS_AUTO_OOB:
+	case MTD_OPS_RAW:
+		break;
+
+	default:
+		goto out;
+	}
+
+	if (!ops->datbuf)
+		ret = spi_nand_do_read_oob(mtd, from, ops);
+	else
+		ret = spi_nand_do_read_ops(mtd, from, ops);
+
+out:
+	spi_nand_release_device(mtd);
+
+	return ret;
+}
+
+/**
+ * spi_nand_write_oob - [MTD Interface] SPI-NAND write data and/or out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ */
+static int spi_nand_write_oob(struct mtd_info *mtd, loff_t to,
+			  struct mtd_oob_ops *ops)
+{
+	int ret = -ENOTSUPP;
+
+	ops->retlen = 0;
+
+	/* Do not allow writes past end of device */
+	if (ops->datbuf && (to + ops->len) > mtd->size) {
+		pr_debug("%s: attempt to write beyond end of device\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	spi_nand_get_device(mtd, FL_WRITING);
+
+	switch (ops->mode) {
+	case MTD_OPS_PLACE_OOB:
+	case MTD_OPS_AUTO_OOB:
+	case MTD_OPS_RAW:
+		break;
+
+	default:
+		goto out;
+	}
+
+	if (!ops->datbuf)
+		ret = spi_nand_do_write_oob(mtd, to, ops);
+	else
+		ret = spi_nand_do_write_ops(mtd, to, ops);
+
+out:
+	spi_nand_release_device(mtd);
+
+	return ret;
+}
+
+/**
+ * spi_nand_block_bad - [INTERN] Check if block at offset is bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ */
+static int spi_nand_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	struct mtd_oob_ops ops = {0};
+	u32 block_addr;
+	u8 bad[2] = {0, 0};
+	u8 ret = 0;
+
+	block_addr = ofs >> chip->block_shift;
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.ooblen = 2;
+	ops.oobbuf = bad;
+
+	ret = spi_nand_do_read_oob(mtd, block_addr << chip->block_shift, &ops);
+	if (bad[0] != 0xFF || bad[1] != 0xFF)
+		ret =  1;
+
+	return ret;
+
+}
+
+/**
+ * spi_nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @allowbbt: 1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int spi_nand_block_checkbad(struct mtd_info *mtd, loff_t ofs,
+			       int allowbbt)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+
+	if (!chip->bbt)
+		return spi_nand_block_bad(mtd, ofs);
+
+	/* Return info from the table */
+	return spi_nand_isbad_bbt(mtd, ofs, allowbbt);
+}
+
+/**
+ * spi_nand_block_isbad - [MTD Interface] Check if block at offset is bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ */
+static int spi_nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+{
+	return spi_nand_block_checkbad(mtd, offs, 0);
+}
+
+/**
+ * spi_nand_block_markbad_lowlevel - mark a block bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * This function performs the generic bad block marking steps (i.e., bad
+ * block table(s) and/or marker(s)). We only allow the hardware driver to
+ * specify how to write bad block markers to OOB (chip->block_markbad).
+ *
+ * We try operations in the following order:
+ *  (1) erase the affected block, to allow OOB marker to be written cleanly
+ *  (2) write bad block marker to OOB area of affected block (unless flag
+ *      NAND_BBT_NO_OOB_BBM is present)
+ *  (3) update the BBT
+ * Note that we retain the first error encountered in (2) or (3), finish the
+ * procedures, and dump the error in the end.
+*/
+static int spi_nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	struct mtd_oob_ops ops = {0};
+	struct erase_info einfo = {0};
+	u32 block_addr;
+	u8 buf[2] = {0, 0};
+	int res, ret = 0;
+
+	if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
+		/*erase bad block before mark bad block*/
+		einfo.mtd = mtd;
+		einfo.addr = ofs;
+		einfo.len = 1UL << chip->block_shift;
+		spi_nand_erase(mtd, &einfo);
+
+		block_addr = ofs >> chip->block_shift;
+		ops.mode = MTD_OPS_PLACE_OOB;
+		ops.ooblen = 2;
+		ops.oobbuf = buf;
+
+		ret = spi_nand_do_write_oob(mtd,
+				block_addr << chip->block_shift, &ops);
+	}
+
+	/* Mark block bad in BBT */
+	if (chip->bbt) {
+		res = spi_nand_markbad_bbt(mtd, ofs);
+		if (!ret)
+			ret = res;
+	}
+
+	if (!ret)
+		mtd->ecc_stats.badblocks++;
+
+	return ret;
+}
+
+/**
+ * spi_nand_block_markbad - [MTD Interface] Mark block at the given offset
+ * as bad
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ */
+static int spi_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	int ret;
+
+	ret = spi_nand_block_isbad(mtd, ofs);
+	if (ret) {
+		/* If it was bad already, return success and do nothing */
+		if (ret > 0)
+			return 0;
+		return ret;
+	}
+
+	return spi_nand_block_markbad_lowlevel(mtd, ofs);
+}
+
+/**
+ * __spi_nand_erase - [INTERN] erase block(s)
+ * @mtd: MTD device structure
+ * @einfo: erase instruction
+ * @allowbbt: allow to access bbt
+ *
+ * Erase one ore more blocks
+ */
+int __spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo,
+			int allowbbt)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	int page_addr, pages_per_block;
+	loff_t len;
+	u8 status;
+	int ret = 0;
+
+
+	/* check address align on block boundary */
+	if (einfo->addr & (chip->block_size - 1)) {
+		pr_debug("%s: Unaligned address\n", __func__);
+		return -EINVAL;
+	}
+
+	if (einfo->len & (chip->block_size - 1)) {
+		pr_debug("%s: Length not block aligned\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Do not allow erase past end of device */
+	if ((einfo->len + einfo->addr) > chip->size) {
+		pr_debug("%s: Erase past end of device\n", __func__);
+		return -EINVAL;
+	}
+
+	einfo->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+
+	/* Grab the lock and see if the device is available */
+	spi_nand_get_device(mtd, FL_ERASING);
+
+	pages_per_block = 1 << (chip->block_shift - chip->page_shift);
+	page_addr = einfo->addr >> chip->page_shift;
+	len = einfo->len;
+
+	einfo->state = MTD_ERASING;
+
+	while (len) {
+		/* Check if we have a bad block, we do not erase bad blocks! */
+		if (spi_nand_block_checkbad(mtd, ((loff_t) page_addr) <<
+					chip->page_shift, allowbbt)) {
+			pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
+				    __func__, page_addr);
+			einfo->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}
+		/*
+		 * Invalidate the page cache, if we erase the block which
+		 * contains the current cached page.
+		 */
+		if (page_addr <= chip->pagebuf && chip->pagebuf <
+		    (page_addr + pages_per_block))
+			chip->pagebuf = -1;
+
+		ret = chip->write_enable(chip);
+		if (ret < 0) {
+			pr_debug("write enable command failed\n");
+			einfo->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}
+
+		ret = chip->erase_block(chip, page_addr);
+		if (ret < 0) {
+			pr_debug("block erase command failed\n");
+			einfo->state = MTD_ERASE_FAILED;
+			einfo->fail_addr = (loff_t)page_addr
+						<< chip->page_shift;
+			goto erase_exit;
+		}
+		ret = chip->waitfunc(chip, &status);
+		if (ret < 0) {
+			pr_debug("block erase command wait failed\n");
+			einfo->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}
+		if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
+			pr_debug("erase block 0x%012llx failed\n",
+				((loff_t) page_addr) << chip->page_shift);
+			einfo->state = MTD_ERASE_FAILED;
+			einfo->fail_addr = (loff_t)page_addr
+						<< chip->page_shift;
+			goto erase_exit;
+		}
+
+		/* Increment page address and decrement length */
+		len -= (1ULL << chip->block_shift);
+		page_addr += pages_per_block;
+	}
+
+	einfo->state = MTD_ERASE_DONE;
+
+erase_exit:
+
+	ret = einfo->state == MTD_ERASE_DONE ? 0 : -EIO;
+
+	spi_nand_release_device(mtd);
+
+	/* Do call back function */
+	if (!ret)
+		mtd_erase_callback(einfo);
+
+	/* Return more or less happy */
+	return ret;
+}
+
+/**
+ * spi_nand_erase - [MTD Interface] erase block(s)
+ * @mtd: MTD device structure
+ * @einfo: erase instruction
+ *
+ * Erase one ore more blocks
+ */
+static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo)
+{
+	return __spi_nand_erase(mtd, einfo, 0);
+}
+
+/**
+ * spi_nand_sync - [MTD Interface] sync
+ * @mtd: MTD device structure
+ *
+ * Sync is actually a wait for chip ready function
+ */
+static void spi_nand_sync(struct mtd_info *mtd)
+{
+	pr_debug("spi_nand_sync: called\n");
+
+	/* Grab the lock and see if the device is available */
+	spi_nand_get_device(mtd, FL_SYNCING);
+
+	/* Release it and go back */
+	spi_nand_release_device(mtd);
+}
+
+/**
+ * spi_nand_suspend - [MTD Interface] Suspend the SPI-NAND flash
+ * @mtd: MTD device structure
+ */
+static int spi_nand_suspend(struct mtd_info *mtd)
+{
+	return spi_nand_get_device(mtd, FL_PM_SUSPENDED);
+}
+
+/**
+ * spi_nand_resume - [MTD Interface] Resume the SPI-NAND flash
+ * @mtd: MTD device structure
+ */
+static void spi_nand_resume(struct mtd_info *mtd)
+{
+	struct spi_nand_chip *this = mtd->priv;
+
+	if (this->state == FL_PM_SUSPENDED)
+		spi_nand_release_device(mtd);
+	else
+		pr_err("%s is not called in suspended state\n:", __func__);
+}
+
+/**
+ * spi_nand_scan_id_table - [INTERN] scan chip info in id table
+ * @chip: SPI-NAND device structure
+ * @id: point to manufacture id and device id
+ */
+static bool spi_nand_scan_id_table(struct spi_nand_chip *chip, u8 *id)
+{
+	struct spi_nand_flash *type = spi_nand_table;
+
+	for (; type->name; type++) {
+		if (id[0] == type->mfr_id && id[1] == type->dev_id) {
+			chip->name = type->name;
+			chip->size = type->page_size * type->pages_per_blk
+					* type->blks_per_chip;
+			chip->block_size = type->page_size
+					* type->pages_per_blk;
+			chip->page_size = type->page_size;
+			chip->page_spare_size = type->page_spare_size;
+			chip->block_shift = ilog2(chip->block_size);
+			chip->page_shift = ilog2(chip->page_size);
+			chip->page_mask = chip->page_size - 1;
+			chip->options = type->options;
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * spi_nand_send_cmd - to process a command to send to the SPI-NAND
+ * @spi: spi device structure
+ * @cmd: command structure
+ *
+ *    Set up the command buffer to send to the SPI controller.
+ *    The command buffer has to initialized to 0.
+ */
+int spi_nand_send_cmd(struct spi_device *spi, struct spi_nand_cmd *cmd)
+{
+	struct spi_message message;
+	struct spi_transfer x[4];
+
+	spi_message_init(&message);
+	memset(x, 0, sizeof(x));
+	x[0].len = 1;
+	x[0].tx_buf = &cmd->cmd;
+	spi_message_add_tail(&x[0], &message);
+
+	if (cmd->n_addr) {
+		x[1].len = cmd->n_addr;
+		x[1].tx_buf = cmd->addr;
+		spi_message_add_tail(&x[1], &message);
+	}
+
+	if (cmd->n_tx) {
+		x[2].len = cmd->n_tx;
+		x[2].tx_nbits = cmd->tx_nbits;
+		x[2].tx_buf = cmd->tx_buf;
+		spi_message_add_tail(&x[2], &message);
+	}
+
+	if (cmd->n_rx) {
+		x[3].len = cmd->n_rx;
+		x[3].rx_nbits = cmd->rx_nbits;
+		x[3].rx_buf = cmd->rx_buf;
+		spi_message_add_tail(&x[3], &message);
+	}
+	return spi_sync(spi, &message);
+}
+
+/*
+ * spi_nand_read_status- send command 0x0f to the SPI-NAND status register value
+ * @spi: spi device structure
+ * @status: buffer to store value
+ * Description:
+ *    After read, write, or erase, the Nand device is expected to set the
+ *    busy status.
+ *    This function is to allow reading the status of the command: read,
+ *    write, and erase.
+ *    Once the status turns to be ready, the other status bits also are
+ *    valid status bits.
+ */
+static int spi_nand_read_status(struct spi_device *spi, uint8_t *status)
+{
+	struct spi_nand_cmd cmd = {0};
+	int ret;
+
+	cmd.cmd = SPINAND_CMD_READ_REG;
+	cmd.n_addr = 1;
+	cmd.addr[0] = REG_STATUS;
+	cmd.n_rx = 1;
+	cmd.rx_buf = status;
+
+	ret = spi_nand_send_cmd(spi, &cmd);
+	if (ret < 0)
+		dev_err(&spi->dev, "err: %d read status register\n", ret);
+
+	return ret;
+}
+
+/**
+ * spi_nand_get_otp- send command 0x0f to read the SPI-NAND OTP register
+ * @spi: spi device structure
+ * @opt: buffer to store value
+ * Description:
+ *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ *   Enable chip internal ECC, set the bit to 1
+ *   Disable chip internal ECC, clear the bit to 0
+ */
+static int spi_nand_get_otp(struct spi_device *spi, u8 *otp)
+{
+	struct spi_nand_cmd cmd = {0};
+	int ret;
+
+	cmd.cmd = SPINAND_CMD_READ_REG;
+	cmd.n_addr = 1;
+	cmd.addr[0] = REG_OTP;
+	cmd.n_rx = 1;
+	cmd.rx_buf = otp;
+
+	ret = spi_nand_send_cmd(spi, &cmd);
+	if (ret < 0)
+		dev_err(&spi->dev, "error %d get otp\n", ret);
+	return ret;
+}
+
+/**
+ * spi_nand_set_otp- send command 0x1f to write the SPI-NAND OTP register
+ * @spi: spi device structure
+ * @status: buffer stored value
+ * Description:
+ *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ *   Enable chip internal ECC, set the bit to 1
+ *   Disable chip internal ECC, clear the bit to 0
+ */
+static int spi_nand_set_otp(struct spi_device *spi, u8 *otp)
+{
+	int ret;
+	struct spi_nand_cmd cmd = {0};
+
+	cmd.cmd = SPINAND_CMD_WRITE_REG,
+	cmd.n_addr = 1,
+	cmd.addr[0] = REG_OTP,
+	cmd.n_tx = 1,
+	cmd.tx_buf = otp,
+
+	ret = spi_nand_send_cmd(spi, &cmd);
+	if (ret < 0)
+		dev_err(&spi->dev, "error %d set otp\n", ret);
+
+	return ret;
+}
+
+/**
+ * spi_nand_enable_ecc- enable internal ECC
+ * @chip: SPI-NAND device structure
+ * Description:
+ *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ *   Enable chip internal ECC, set the bit to 1
+ *   Disable chip internal ECC, clear the bit to 0
+ */
+static int spi_nand_enable_ecc(struct spi_nand_chip *chip)
+{
+	struct spi_device *spi = chip->spi;
+	int ret;
+	u8 otp = 0;
+
+	ret = spi_nand_get_otp(spi, &otp);
+	if (ret < 0)
+		return ret;
+
+	if ((otp & OTP_ECC_MASK) == OTP_ECC_ENABLE)
+		return 0;
+
+	otp |= OTP_ECC_ENABLE;
+	ret = spi_nand_set_otp(spi, &otp);
+	if (ret < 0)
+		return ret;
+	return spi_nand_get_otp(spi, &otp);
+}
+
+/**
+ * spi_nand_disable_ecc- disable internal ECC
+ * @chip: SPI-NAND device structure
+ * Description:
+ *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ *   Enable chip internal ECC, set the bit to 1
+ *   Disable chip internal ECC, clear the bit to 0
+ */
+static int spi_nand_disable_ecc(struct spi_nand_chip *chip)
+{
+	struct spi_device *spi = chip->spi;
+	int ret;
+	u8 otp = 0;
+
+	ret = spi_nand_get_otp(spi, &otp);
+	if (ret < 0)
+		return ret;
+
+	if ((otp & OTP_ECC_MASK) == OTP_ECC_ENABLE) {
+		otp &= ~OTP_ECC_ENABLE;
+		ret = spi_nand_set_otp(spi, &otp);
+		if (ret < 0)
+			return ret;
+		return spi_nand_get_otp(spi, &otp);
+	} else
+		return 0;
+}
+
+/**
+ * spi_nand_write_enable- send command 0x06 to enable write or erase the
+ * Nand cells
+ * @chip: SPI-NAND device structure
+ * Description:
+ *   Before write and erase the Nand cells, the write enable has to be set.
+ *   After the write or erase, the write enable bit is automatically
+ *   cleared (status register bit 2)
+ *   Set the bit 2 of the status register has the same effect
+ */
+static int spi_nand_write_enable(struct spi_nand_chip *chip)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_WR_ENABLE;
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+/*
+ * spi_nand_read_from_cache- send command 0x13 to read data from Nand to cache
+ * @chip: SPI-NAND device structure
+ * @page_addr: page to read
+ */
+static int spi_nand_read_page_to_cache(struct spi_nand_chip *chip,
+					u32 page_addr)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_READ;
+	cmd.n_addr = 3;
+	cmd.addr[0] = (u8)(page_addr >> 16);
+	cmd.addr[1] = (u8)(page_addr >> 8);
+	cmd.addr[2] = (u8)page_addr;
+
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+/*
+ * spi_nand_read_from_cache- send command 0x03 to read out the data from the
+ * cache register
+ * Description:
+ *   The read can specify 1 to (page size + spare size) bytes of data read at
+ *   the corresponding locations.
+ *   No tRd delay.
+ */
+int spi_nand_read_from_cache(struct spi_nand_chip *chip, u32 page_addr,
+		u32 column, size_t len, u8 *rbuf)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_READ_RDM;
+	cmd.n_addr = 3;
+	cmd.addr[0] = (u8)(column >> 8);
+	if (chip->options & SPINAND_NEED_PLANE_SELECT)
+		cmd.addr[0] |= (u8)(((page_addr >>
+			(chip->block_shift - chip->page_shift)) & 0x1) << 4);
+	cmd.addr[1] = (u8)column;
+	cmd.addr[2] = 0;
+	cmd.n_rx = len;
+	cmd.rx_buf = rbuf;
+
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+/*
+ * spi_nand_read_from_cache_x2- send command 0x3b to read out the data from the
+ * cache register
+ * Description:
+ *   The read can specify 1 to (page size + spare size) bytes of data read at
+ *   the corresponding locations.
+ *   No tRd delay.
+ */
+int spi_nand_read_from_cache_x2(struct spi_nand_chip *chip, u32 page_addr,
+		u32 column, size_t len, u8 *rbuf)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_READ_CACHE_X2;
+	cmd.n_addr = 3;
+	cmd.addr[0] = (u8)(column >> 8);
+	if (chip->options & SPINAND_NEED_PLANE_SELECT)
+		cmd.addr[0] |= (u8)(((page_addr >>
+			(chip->block_shift - chip->page_shift)) & 0x1) << 4);
+	cmd.addr[1] = (u8)column;
+	cmd.addr[2] = 0;
+	cmd.n_rx = len;
+	cmd.rx_nbits = SPI_NBITS_DUAL;
+	cmd.rx_buf = rbuf;
+
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+/*
+ * spi_nand_read_from_cache_x4- send command 0x6b to read out the data from the
+ * cache register
+ * Description:
+ *   The read can specify 1 to (page size + spare size) bytes of data read at
+ *   the corresponding locations.
+ *   No tRd delay.
+ */
+int spi_nand_read_from_cache_x4(struct spi_nand_chip *chip, u32 page_addr,
+		u32 column, size_t len, u8 *rbuf)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_READ_CACHE_X4;
+	cmd.n_addr = 3;
+	cmd.addr[0] = (u8)(column >> 8);
+	if (chip->options & SPINAND_NEED_PLANE_SELECT)
+		cmd.addr[0] |= (u8)(((page_addr >>
+			(chip->block_shift - chip->page_shift)) & 0x1) << 4);
+	cmd.addr[1] = (u8)column;
+	cmd.addr[2] = 0;
+	cmd.n_rx = len;
+	cmd.rx_nbits = SPI_NBITS_QUAD;
+	cmd.rx_buf = rbuf;
+
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+/*
+ * spi_nand_read_from_cache_snor_protocol- send command 0x03 to read out the
+ * data from the cache register, 0x03 command protocol is same as SPI NOR
+ * read command
+ * Description:
+ *   The read can specify 1 to (page size + spare size) bytes of data read at
+ *   the corresponding locations.
+ *   No tRd delay.
+ */
+int spi_nand_read_from_cache_snor_protocol(struct spi_nand_chip *chip,
+		u32 page_addr, u32 column, size_t len, u8 *rbuf)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_READ_RDM;
+	cmd.n_addr = 3;
+	cmd.addr[0] = 0;
+	cmd.addr[1] = (u8)(column >> 8);
+	if (chip->options & SPINAND_NEED_PLANE_SELECT)
+		cmd.addr[1] |= (u8)(((page_addr >>
+			(chip->block_shift - chip->page_shift)) & 0x1) << 4);
+	cmd.addr[2] = (u8)column;
+	cmd.n_rx = len;
+	cmd.rx_buf = rbuf;
+
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+
+/*
+ * spi_nand_program_data_to_cache--to write a page to cache
+ * @chip: SPI-NAND device structure
+ * @page_addr: page to write
+ * @column: the location to write to the cache
+ * @len: number of bytes to write
+ * wrbuf: buffer held @len bytes
+ *
+ * Description:
+ *   The write command used here is 0x02--indicating that the cache is
+ *   cleared first.
+ *   Since it is writing the data to cache, there is no tPROG time.
+ */
+static int spi_nand_program_data_to_cache(struct spi_nand_chip *chip,
+		u32 page_addr, u32 column, size_t len, u8 *wbuf)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_PROG_PAGE_CLRCACHE;
+	cmd.n_addr = 2;
+	cmd.addr[0] = (u8)(column >> 8);
+	if (chip->options & SPINAND_NEED_PLANE_SELECT)
+		cmd.addr[0] |= (u8)(((page_addr >>
+			(chip->block_shift - chip->page_shift)) & 0x1) << 4);
+	cmd.addr[1] = (u8)column;
+	cmd.n_tx = len;
+	cmd.tx_buf = wbuf;
+
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+/**
+ * spi_nand_program_execute--to write a page from cache to the Nand array
+ * @chip: SPI-NAND device structure
+ * @page_addr: the physical page location to write the page.
+ *
+ * Description:
+ *   The write command used here is 0x10--indicating the cache is writing to
+ *   the Nand array.
+ *   Need to wait for tPROG time to finish the transaction.
+ */
+static int spi_nand_program_execute(struct spi_nand_chip *chip, u32 page_addr)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_PROG_PAGE_EXC;
+	cmd.n_addr = 3;
+	cmd.addr[0] = (u8)(page_addr >> 16);
+	cmd.addr[1] = (u8)(page_addr >> 8);
+	cmd.addr[2] = (u8)page_addr;
+
+
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+
+/**
+ * spi_nand_erase_block_erase--to erase a block
+ * @chip: SPI-NAND device structure
+ * @page_addr: the page to erase.
+ *
+ * Description:
+ *   The command used here is 0xd8--indicating an erase command to erase
+ *   one block
+ *   Need to wait for tERS.
+ */
+static int spi_nand_erase_block(struct spi_nand_chip *chip,
+					u32 page_addr)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_ERASE_BLK;
+	cmd.n_addr = 3;
+	cmd.addr[0] = (u8)(page_addr >> 16);
+	cmd.addr[1] = (u8)(page_addr >> 8);
+	cmd.addr[2] = (u8)page_addr;
+
+	return spi_nand_send_cmd(spi, &cmd);
+}
+
+/**
+ * spi_nand_wait - [DEFAULT] wait until the command is done
+ * @chip: SPI-NAND device structure
+ * @s: buffer to store status register(can be NULL)
+ *
+ * Wait for command done. This applies to erase and program only. Erase can
+ * take up to 400ms and program up to 20ms.
+ */
+static int spi_nand_wait(struct spi_nand_chip *chip, u8 *s)
+{
+	unsigned long timeo = jiffies;
+	u8 status, state = chip->state;
+	int ret = -ETIMEDOUT;
+
+	if (state == FL_ERASING)
+		timeo += msecs_to_jiffies(400);
+	else
+		timeo += msecs_to_jiffies(20);
+
+	while (time_before(jiffies, timeo)) {
+		spi_nand_read_status(chip->spi, &status);
+		if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+			ret = 0;
+			goto out;
+		}
+		cond_resched();
+	}
+out:
+	if (s)
+		*s = status;
+
+	return ret;
+}
+
+
+/*
+ * spi_nand_reset- send RESET command "0xff" to the SPI-NAND.
+ * @chip: SPI-NAND device structure
+ */
+static int spi_nand_reset(struct spi_nand_chip *chip)
+{
+	struct spi_nand_cmd cmd = {0};
+	struct spi_device *spi = chip->spi;
+
+	cmd.cmd = SPINAND_CMD_RESET;
+
+	if (spi_nand_send_cmd(spi, &cmd) < 0)
+		pr_err("spi_nand reset failed!\n");
+
+	/* elapse 1ms before issuing any other command */
+	udelay(1000);
+
+	return 0;
+}
+
+
+/**
+ * spi_nand_lock_block- send write register 0x1f command to the lock/unlock device
+ * @spi: spi device structure
+ * @lock: value to set to block lock register
+ *
+ * Description:
+ *    After power up, all the Nand blocks are locked.  This function allows
+ *    one to unlock the blocks, and so it can be written or erased.
+ */
+static int spi_nand_lock_block(struct spi_device *spi, u8 lock)
+{
+	struct spi_nand_cmd cmd = {0};
+	int ret;
+
+	cmd.cmd = SPINAND_CMD_WRITE_REG;
+	cmd.n_addr = 1;
+	cmd.addr[0] = REG_BLOCK_LOCK;
+	cmd.n_tx = 1;
+	cmd.tx_buf = &lock;
+
+	ret = spi_nand_send_cmd(spi, &cmd);
+	if (ret < 0)
+		dev_err(&spi->dev, "error %d lock block\n", ret);
+
+	return ret;
+}
+
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+	int i;
+
+	while (len--) {
+		crc ^= *p++ << 8;
+		for (i = 0; i < 8; i++)
+			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+	}
+
+	return crc;
+}
+
+/* Sanitize ONFI strings so we can safely print them */
+static void sanitize_string(uint8_t *s, size_t len)
+{
+	ssize_t i;
+
+	/* Null terminate */
+	s[len - 1] = 0;
+
+	/* Remove non printable chars */
+	for (i = 0; i < len - 1; i++) {
+		if (s[i] < ' ' || s[i] > 127)
+			s[i] = '?';
+	}
+
+	/* Remove trailing spaces */
+	strim(s);
+}
+
+/*
+ * Check if the SPI-NAND chip is ONFI compliant,
+ * returns 1 if it is, 0 otherwise.
+ */
+static bool spi_nand_detect_onfi(struct spi_nand_chip *chip)
+{
+	struct spi_device *spi = chip->spi;
+	struct spi_nand_onfi_params *p;
+	char *buffer;
+	bool ret = true;
+	u8 otp;
+	int i;
+
+	/*FIXME buffer size*/
+	buffer = kmalloc(256*3, GFP_KERNEL);
+	otp = OTP_ENABLE;
+	spi_nand_set_otp(spi, &otp);
+	chip->load_page(chip, 0x01);
+	chip->waitfunc(chip, NULL);
+	spi_nand_read_from_cache(chip, 0x01, 0x00, 256*3, buffer);
+	otp = OTP_ECC_ENABLE;
+	spi_nand_set_otp(spi, &otp);
+
+	p = (struct spi_nand_onfi_params *)buffer;
+	for (i = 0; i < 3; i++, p++) {
+		if (p->sig[0] != 'O' || p->sig[1] != 'N' ||
+				p->sig[2] != 'F' || p->sig[3] != 'I')
+			continue;
+		if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+				le16_to_cpu(p->crc))
+			break;
+	}
+	if (i == 3) {
+		pr_err("Could not find valid ONFI parameter page; aborting\n");
+		ret = false;
+		goto out;
+	}
+
+	memcpy(&chip->onfi_params, p, sizeof(*p));
+
+	p = &chip->onfi_params;
+
+	sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+	sanitize_string(p->model, sizeof(p->model));
+
+	chip->name = p->model;
+	chip->size = le32_to_cpu(p->byte_per_page) *
+			le32_to_cpu(p->pages_per_block) *
+			le32_to_cpu(p->blocks_per_lun) * p->lun_count;
+	chip->block_size = le32_to_cpu(p->byte_per_page) *
+			le32_to_cpu(p->pages_per_block);
+	chip->page_size = le32_to_cpu(p->byte_per_page);
+	chip->page_spare_size = le16_to_cpu(p->spare_bytes_per_page);
+	chip->block_shift = ilog2(chip->block_size);
+	chip->page_shift = ilog2(chip->page_size);
+	chip->page_mask = chip->page_size - 1;
+	chip->bits_per_cell = p->bits_per_cell;
+	/*FIXME need to find a way to read options from ONFI table*/
+	chip->options = SPINAND_NEED_PLANE_SELECT;
+	if (p->ecc_bits != 0xff) {
+		chip->ecc_strength_ds = p->ecc_bits;
+		chip->ecc_step_ds = 512;
+	}
+
+out:
+	kfree(buffer);
+	return ret;
+}
+
+static void spi_nand_set_defaults(struct spi_nand_chip *chip)
+{
+	struct spi_device *spi = chip->spi;
+
+	if (spi->mode & SPI_RX_QUAD)
+		chip->read_cache = spi_nand_read_from_cache_x4;
+	else if (spi->mode & SPI_RX_DUAL)
+		chip->read_cache = spi_nand_read_from_cache_x2;
+	else
+		chip->read_cache = spi_nand_read_from_cache;
+
+	if (!chip->reset)
+		chip->reset = spi_nand_reset;
+	if (!chip->erase_block)
+		chip->erase_block = spi_nand_erase_block;
+	if (!chip->load_page)
+		chip->load_page = spi_nand_read_page_to_cache;
+	if (!chip->store_cache)
+		chip->store_cache = spi_nand_program_data_to_cache;
+	if (!chip->write_page)
+		chip->write_page = spi_nand_program_execute;
+	if (!chip->write_enable)
+		chip->write_enable = spi_nand_write_enable;
+	if (!chip->waitfunc)
+		chip->waitfunc = spi_nand_wait;
+	if (!chip->enable_ecc)
+		chip->enable_ecc = spi_nand_enable_ecc;
+	if (!chip->disable_ecc)
+		chip->disable_ecc = spi_nand_disable_ecc;
+}
+
+static int spi_nand_check(struct spi_nand_chip *chip)
+{
+	if (!chip->reset)
+		return -ENODEV;
+	if (!chip->read_id)
+		return -ENODEV;
+	if (!chip->load_page)
+		return -ENODEV;
+	if (!chip->read_cache)
+		return -ENODEV;
+	if (!chip->store_cache)
+		return -ENODEV;
+	if (!chip->write_page)
+		return -ENODEV;
+	if (!chip->erase_block)
+		return -ENODEV;
+	if (!chip->waitfunc)
+		return -ENODEV;
+	if (!chip->write_enable)
+		return -ENODEV;
+	if (!chip->get_ecc_status)
+		return -ENODEV;
+	if (!chip->enable_ecc)
+		return -ENODEV;
+	if (!chip->disable_ecc)
+		return -ENODEV;
+	if (!chip->ecclayout)
+		return -ENODEV;
+	return 0;
+}
+
+/**
+ * spi_nand_scan_ident - [SPI-NAND Interface] Scan for the SPI-NAND device
+ * @mtd: MTD device structure
+ *
+ * This is the first phase of the normal spi_nand_scan() function. It reads the
+ * flash ID and sets up MTD fields accordingly.
+ *
+ */
+int spi_nand_scan_ident(struct mtd_info *mtd)
+{
+	int ret;
+	u8 id[SPINAND_MAX_ID_LEN] = {0};
+	struct spi_nand_chip *chip = mtd->priv;
+
+	spi_nand_set_defaults(chip);
+	chip->reset(chip);
+	chip->read_id(chip, id);
+	if (id[0] == 0 && id[1] == 0) {
+		pr_err("SPINAND: read id error! 0x%02x, 0x%02x!\n",
+			id[0], id[1]);
+		return -ENODEV;
+	}
+
+	if (spi_nand_scan_id_table(chip, id))
+		goto ident_done;
+	pr_info("SPI-NAND type mfr_id: %x, dev_id: %x is not in id table.\n",
+				id[0], id[1]);
+
+	if (spi_nand_detect_onfi(chip))
+		goto ident_done;
+
+	return -ENODEV;
+
+ident_done:
+	pr_info("SPI-NAND: %s is found.\n", chip->name);
+
+	chip->mfr_id = id[0];
+	chip->dev_id = id[1];
+
+	chip->buf = kzalloc(chip->page_size + chip->page_spare_size,
+				GFP_KERNEL);
+	if (!chip->buf)
+		return -ENOMEM;
+
+	chip->oobbuf = chip->buf + chip->page_size;
+
+	ret = spi_nand_lock_block(chip->spi, BL_ALL_UNLOCKED);
+	ret = chip->enable_ecc(chip);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_nand_scan_ident);
+
+/**
+ * spi_nand_scan_tail - [SPI-NAND Interface] Scan for the SPI-NAND device
+ * @mtd: MTD device structure
+ *
+ * This is the second phase of the normal spi_nand_scan() function. It fills out
+ * all the uninitialized function pointers with the defaults.
+ */
+int spi_nand_scan_tail(struct mtd_info *mtd)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+	int ret;
+
+	ret = spi_nand_check(chip);
+	if (ret)
+		return ret;
+	/* Initialize state */
+	chip->state = FL_READY;
+	/* Invalidate the pagebuffer reference */
+	chip->pagebuf = -1;
+	chip->bbt_options |= NAND_BBT_USE_FLASH;
+	chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
+
+	init_waitqueue_head(&chip->wq);
+	spin_lock_init(&chip->chip_lock);
+
+	mtd->name = chip->name;
+	mtd->size = chip->size;
+	mtd->erasesize = chip->block_size;
+	mtd->writesize = chip->page_size;
+	mtd->writebufsize = mtd->writesize;
+	mtd->oobsize = chip->page_spare_size;
+	mtd->owner = THIS_MODULE;
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	if (!mtd->ecc_strength)
+		mtd->ecc_strength = chip->ecc_strength_ds ?
+					chip->ecc_strength_ds : 1;
+
+	mtd->ecclayout = chip->ecclayout;
+	mtd->oobsize = chip->page_spare_size;
+	mtd->oobavail = chip->ecclayout->oobavail;
+	mtd->_erase = spi_nand_erase;
+	mtd->_point = NULL;
+	mtd->_unpoint = NULL;
+	mtd->_read = spi_nand_read;
+	mtd->_write = spi_nand_write;
+	mtd->_read_oob = spi_nand_read_oob;
+	mtd->_write_oob = spi_nand_write_oob;
+	mtd->_sync = spi_nand_sync;
+	mtd->_lock = NULL;
+	mtd->_unlock = NULL;
+	mtd->_suspend = spi_nand_suspend;
+	mtd->_resume = spi_nand_resume;
+	mtd->_block_isbad = spi_nand_block_isbad;
+	mtd->_block_markbad = spi_nand_block_markbad;
+
+	/* Build bad block table */
+	return spi_nand_default_bbt(mtd);
+}
+EXPORT_SYMBOL_GPL(spi_nand_scan_tail);
+
+/**
+ * spi_nand_scan_ident_release - [SPI-NAND Interface] Free resources
+ * applied by spi_nand_scan_ident
+ * @mtd: MTD device structure
+ */
+int spi_nand_scan_ident_release(struct mtd_info *mtd)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+
+	kfree(chip->buf);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_nand_scan_ident_release);
+
+/**
+ * spi_nand_scan_tail_release - [SPI-NAND Interface] Free resources
+ * applied by spi_nand_scan_tail
+ * @mtd: MTD device structure
+ */
+int spi_nand_scan_tail_release(struct mtd_info *mtd)
+{
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_nand_scan_tail_release);
+
+/**
+ * spi_nand_release - [SPI-NAND Interface] Free resources held by the SPI-NAND
+ * device
+ * @mtd: MTD device structure
+ */
+int spi_nand_release(struct mtd_info *mtd)
+{
+	struct spi_nand_chip *chip = mtd->priv;
+
+	mtd_device_unregister(mtd);
+	kfree(chip->buf);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_nand_release);
+
+MODULE_DESCRIPTION("SPI NAND framework");
+MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nand/spi-nand-bbt.c b/drivers/mtd/spi-nand/spi-nand-bbt.c
new file mode 100644
index 0000000..1a29156
--- /dev/null
+++ b/drivers/mtd/spi-nand/spi-nand-bbt.c
@@ -0,0 +1,1279 @@ 
+/*
+ *  drivers/mtd/spi_nand_bbt.c
+ *
+ *  Overview:
+ *   Bad block table support for the SPI-NAND driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file is derived from nand_base.c
+ *
+ * TODO:
+ *   share BBT code with parallel nand
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/bbm.h>
+#include <linux/mtd/spi-nand.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/export.h>
+#include <linux/string.h>
+
+#define BBT_BLOCK_GOOD		0x00
+#define BBT_BLOCK_WORN		0x01
+#define BBT_BLOCK_RESERVED	0x02
+#define BBT_BLOCK_FACTORY_BAD	0x03
+
+#define BBT_ENTRY_MASK		0x03
+#define BBT_ENTRY_SHIFT		2
+
+static int spi_nand_update_bbt(struct mtd_info *mtd, loff_t offs);
+
+static inline uint8_t bbt_get_entry(struct spi_nand_chip *chip, int block)
+{
+	uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT];
+
+	entry >>= (block & BBT_ENTRY_MASK) * 2;
+	return entry & BBT_ENTRY_MASK;
+}
+
+static inline void bbt_mark_entry(struct spi_nand_chip *chip, int block,
+		uint8_t mark)
+{
+	uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2);
+
+	chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk;
+}
+
+static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
+{
+	if (memcmp(buf, td->pattern, td->len))
+		return -1;
+	return 0;
+}
+
+/**
+ * check_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf: the buffer to search
+ * @len: the length of buffer to search
+ * @paglen: the pagelength
+ * @td: search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers.
+ */
+static int check_pattern(uint8_t *buf, int len, int paglen,
+			struct nand_bbt_descr *td)
+{
+	if (td->options & NAND_BBT_NO_OOB)
+		return check_pattern_no_oob(buf, td);
+
+	/* Compare the pattern */
+	if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
+		return -1;
+
+	return 0;
+}
+
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf: the buffer to search
+ * @td:	search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers. Same as check_pattern, but no optional empty
+ * check.
+ */
+static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
+{
+	/* Compare the pattern */
+	if (memcmp(buf + td->offs, td->pattern, td->len))
+		return -1;
+	return 0;
+}
+
+/**
+ * add_marker_len - compute the length of the marker in data area
+ * @td: BBT descriptor used for computation
+ *
+ * The length will be 0 if the marker is located in OOB area.
+ */
+static u32 add_marker_len(struct nand_bbt_descr *td)
+{
+	u32 len;
+
+	if (!(td->options & NAND_BBT_NO_OOB))
+		return 0;
+
+	len = td->len;
+	if (td->options & NAND_BBT_VERSION)
+		len++;
+	return len;
+}
+
+/**
+ * read_bbt - [GENERIC] Read the bad block table starting from page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @page: the starting page
+ * @num: the number of bbt descriptors to read
+ * @td: the bbt describtion table
+ * @offs: block number offset in the table
+ *
+ * Read the bad block table starting from page.
+ */
+static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
+		struct nand_bbt_descr *td, int offs)
+{
+	int res, ret = 0, i, j, act = 0;
+	struct spi_nand_chip *this = mtd->priv;
+	size_t retlen, len, totlen;
+	loff_t from;
+	int bits = td->options & NAND_BBT_NRBITS_MSK;
+	uint8_t msk = (uint8_t)((1 << bits) - 1);
+	u32 marker_len;
+	int reserved_block_code = td->reserved_block_code;
+
+	totlen = (num * bits) >> 3;
+	marker_len = add_marker_len(td);
+	from = ((loff_t)page) << this->page_shift;
+
+	while (totlen) {
+		len = min(totlen, (size_t)(1 << this->block_shift));
+		if (marker_len) {
+			/*
+			 * In case the BBT marker is not in the OOB area it
+			 * will be just in the first page.
+			 */
+			len -= marker_len;
+			from += marker_len;
+			marker_len = 0;
+		}
+		res = mtd_read(mtd, from, len, &retlen, buf);
+		if (res < 0) {
+			if (mtd_is_eccerr(res)) {
+				pr_info("spi_nand_bbt: ECC error in BBT at 0x%012llx\n",
+					from & ~mtd->writesize);
+				return res;
+			} else if (mtd_is_bitflip(res)) {
+				pr_info("spi_nand_bbt: corrected error in BBT at 0x%012llx\n",
+					from & ~mtd->writesize);
+				ret = res;
+			} else {
+				pr_info("spi_nand_bbt: error reading BBT\n");
+				return res;
+			}
+		}
+
+		/* Analyse data */
+		for (i = 0; i < len; i++) {
+			uint8_t dat = buf[i];
+
+			for (j = 0; j < 8; j += bits, act++) {
+				uint8_t tmp = (dat >> j) & msk;
+
+				if (tmp == msk)
+					continue;
+				if (reserved_block_code &&
+					(tmp == reserved_block_code)) {
+					pr_info("spi_nand_read_bbt: reserved block at 0x%012llx\n",
+						 (loff_t)(offs + act) <<
+						 this->block_shift);
+					bbt_mark_entry(this, offs + act,
+							BBT_BLOCK_RESERVED);
+					mtd->ecc_stats.bbtblocks++;
+					continue;
+				}
+				/*
+				 * Leave it for now, if it's matured we can
+				 * move this message to pr_debug.
+				 */
+				pr_info("spi_nand_read_bbt: bad block at 0x%012llx\n",
+					 (loff_t)(offs + act) <<
+					 this->block_shift);
+				/* Factory marked bad or worn out? */
+				if (tmp == 0)
+					bbt_mark_entry(this, offs + act,
+							BBT_BLOCK_FACTORY_BAD);
+				else
+					bbt_mark_entry(this, offs + act,
+							BBT_BLOCK_WORN);
+				mtd->ecc_stats.badblocks++;
+			}
+		}
+		totlen -= len;
+		from += len;
+	}
+	return ret;
+}
+
+/**
+ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @chip: read the table for a specific chip, -1 read all chips; applies only if
+ *        NAND_BBT_PERCHIP option is set
+ *
+ * Read the bad block table for all chips starting at a given page. We assume
+ * that the bbt bits are in consecutive order.
+ */
+static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf,
+			struct nand_bbt_descr *td, int chip)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int res = 0;
+
+	res = read_bbt(mtd, buf, td->pages[0],
+			mtd->size >> this->block_shift, td, 0);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+/* BBT marker is in the first page, no OOB */
+static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+			 struct nand_bbt_descr *td)
+{
+	size_t retlen;
+	size_t len;
+
+	len = td->len;
+	if (td->options & NAND_BBT_VERSION)
+		len++;
+
+	return mtd_read(mtd, offs, len, &retlen, buf);
+}
+
+/**
+ * scan_read_oob - [GENERIC] Scan data+OOB region to buffer
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @offs: offset at which to scan
+ * @len: length of data region to read
+ *
+ * Scan read data from data+OOB. May traverse multiple pages, interleaving
+ * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
+ * ECC condition (error or bitflip). May quit on the first (non-ECC) error.
+ */
+static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+			 size_t len)
+{
+	struct mtd_oob_ops ops;
+	int res, ret = 0;
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.ooboffs = 0;
+	ops.ooblen = mtd->oobsize;
+
+	while (len > 0) {
+		ops.datbuf = buf;
+		ops.len = min_t(size_t, len, mtd->writesize);
+		ops.oobbuf = buf + ops.len;
+
+		res = mtd_read_oob(mtd, offs, &ops);
+		if (res) {
+			if (!mtd_is_bitflip_or_eccerr(res))
+				return res;
+			else if (mtd_is_eccerr(res) || !ret)
+				ret = res;
+		}
+
+		buf += mtd->oobsize + mtd->writesize;
+		len -= mtd->writesize;
+		offs += mtd->writesize;
+	}
+	return ret;
+}
+
+static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+			 size_t len, struct nand_bbt_descr *td)
+{
+	if (td->options & NAND_BBT_NO_OOB)
+		return scan_read_data(mtd, buf, offs, td);
+	else
+		return scan_read_oob(mtd, buf, offs, len);
+}
+
+/* Scan write data with oob to flash */
+static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
+			  uint8_t *buf, uint8_t *oob)
+{
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.ooboffs = 0;
+	ops.ooblen = mtd->oobsize;
+	ops.datbuf = buf;
+	ops.oobbuf = oob;
+	ops.len = len;
+
+	return mtd_write_oob(mtd, offs, &ops);
+}
+
+static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+	u32 ver_offs = td->veroffs;
+
+	if (!(td->options & NAND_BBT_NO_OOB))
+		ver_offs += mtd->writesize;
+	return ver_offs;
+}
+
+/**
+ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md:	descriptor for the bad block table mirror
+ *
+ * Read the bad block table(s) for all chips starting at a given page. We
+ * assume that the bbt bits are in consecutive order.
+ */
+static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+			  struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+	struct spi_nand_chip *this = mtd->priv;
+
+	/* Read the primary version, if available */
+	if (td->options & NAND_BBT_VERSION) {
+		scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
+			      mtd->writesize, td);
+		td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
+		pr_info("Bad block table at page %d, version 0x%02X\n",
+			 td->pages[0], td->version[0]);
+	}
+
+	/* Read the mirror version, if available */
+	if (md && (md->options & NAND_BBT_VERSION)) {
+		scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
+			      mtd->writesize, md);
+		md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
+		pr_info("Bad block table at page %d, version 0x%02X\n",
+			 md->pages[0], md->version[0]);
+	}
+}
+
+/* Scan a given block partially */
+static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+			   loff_t offs, uint8_t *buf, int numpages)
+{
+	struct mtd_oob_ops ops;
+	int j, ret;
+
+	ops.ooblen = mtd->oobsize;
+	ops.oobbuf = buf;
+	ops.ooboffs = 0;
+	ops.datbuf = NULL;
+	ops.mode = MTD_OPS_PLACE_OOB;
+
+	for (j = 0; j < numpages; j++) {
+		/*
+		 * Read the full oob until read_oob is fixed to handle single
+		 * byte reads for 16 bit buswidth.
+		 */
+		ret = mtd_read_oob(mtd, offs, &ops);
+		/* Ignore ECC errors when checking for BBM */
+		if (ret && !mtd_is_bitflip_or_eccerr(ret))
+			return ret;
+
+		if (check_short_pattern(buf, bd))
+			return 1;
+
+		offs += mtd->writesize;
+	}
+	return 0;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ * @chip: create the table for a specific chip, -1 read all chips; applies only
+ *        if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device for the given good/bad block
+ * identify pattern.
+ */
+static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+	struct nand_bbt_descr *bd, int chip)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int i, numblocks, numpages;
+	int startblock;
+	loff_t from;
+
+	pr_info("Scanning device for bad blocks\n");
+
+	if (bd->options & NAND_BBT_SCAN2NDPAGE)
+		numpages = 2;
+	else
+		numpages = 1;
+
+	if (chip == -1) {
+		numblocks = mtd->size >> this->block_shift;
+		startblock = 0;
+		from = 0;
+	} else {
+		numblocks = this->size >> this->block_shift;
+		startblock = chip * numblocks;
+		numblocks += startblock;
+		from = (loff_t)startblock << this->block_shift;
+	}
+
+	if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
+		from += mtd->erasesize - (mtd->writesize * numpages);
+
+	for (i = startblock; i < numblocks; i++) {
+		int ret;
+
+		BUG_ON(bd->options & NAND_BBT_NO_OOB);
+
+		ret = scan_block_fast(mtd, bd, from, buf, numpages);
+		if (ret < 0)
+			return ret;
+
+		if (ret) {
+			bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD);
+			pr_warn("Bad eraseblock %d at 0x%012llx\n",
+				i, (unsigned long long)from);
+			mtd->ecc_stats.badblocks++;
+		}
+
+		from += (1 << this->block_shift);
+	}
+	return 0;
+}
+
+/**
+ * search_bbt - [GENERIC] scan the device for a specific bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ *
+ * Read the bad block table by searching for a given ident pattern. Search is
+ * preformed either from the beginning up or from the end of the device
+ * downwards. The search starts always at the start of a block. If the option
+ * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains
+ * the bad block information of this chip. This is necessary to provide support
+ * for certain DOC devices.
+ *
+ * The bbt ident pattern resides in the oob area of the first page in a block.
+ */
+static int search_bbt(struct mtd_info *mtd, uint8_t *buf,
+			struct nand_bbt_descr *td)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int i, chips;
+	int startblock, block, dir;
+	int scanlen = mtd->writesize + mtd->oobsize;
+	int bbtblocks;
+	int blocktopage = this->block_shift - this->page_shift;
+
+	/* Search direction top -> down? */
+	if (td->options & NAND_BBT_LASTBLOCK) {
+		startblock = (mtd->size >> this->block_shift) - 1;
+		dir = -1;
+	} else {
+		startblock = 0;
+		dir = 1;
+	}
+
+	chips = 1;
+	bbtblocks = mtd->size >> this->block_shift;
+
+	for (i = 0; i < chips; i++) {
+		/* Reset version information */
+		td->version[i] = 0;
+		td->pages[i] = -1;
+		/* Scan the maximum number of blocks */
+		for (block = 0; block < td->maxblocks; block++) {
+
+			int actblock = startblock + dir * block;
+			loff_t offs = (loff_t)actblock << this->block_shift;
+
+			/* Read first page */
+			scan_read(mtd, buf, offs, mtd->writesize, td);
+			if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
+				td->pages[i] = actblock << blocktopage;
+				if (td->options & NAND_BBT_VERSION) {
+					offs = bbt_get_ver_offs(mtd, td);
+					td->version[i] = buf[offs];
+				}
+				break;
+			}
+		}
+		startblock += this->size >> this->block_shift;
+	}
+	/* Check, if we found a bbt for each requested chip */
+	for (i = 0; i < chips; i++) {
+		if (td->pages[i] == -1)
+			pr_warn("Bad block table not found for chip %d\n", i);
+		else
+			pr_info("Bad block table found at page %d, version 0x%02X\n",
+				td->pages[i], td->version[i]);
+	}
+	return 0;
+}
+
+/**
+ * search_read_bbts - [GENERIC] scan the device for bad block table(s)
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ *
+ * Search and read the bad block table(s).
+ */
+static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
+			     struct nand_bbt_descr *td,
+			     struct nand_bbt_descr *md)
+{
+	/* Search the primary table */
+	search_bbt(mtd, buf, td);
+
+	/* Search the mirror table */
+	if (md)
+		search_bbt(mtd, buf, md);
+}
+
+/**
+ * write_bbt - [GENERIC] (Re)write the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ * @chipsel: selector for a specific chip, -1 for all
+ *
+ * (Re)write the bad block table.
+ */
+static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+		     struct nand_bbt_descr *td, struct nand_bbt_descr *md,
+		     int chipsel)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	struct erase_info einfo;
+	int i, res, chip = 0;
+	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+	int nrchips, pageoffs, ooboffs;
+	uint8_t msk[4];
+	uint8_t rcode = td->reserved_block_code;
+	size_t retlen, len = 0;
+	loff_t to;
+	struct mtd_oob_ops ops;
+
+	ops.ooblen = mtd->oobsize;
+	ops.ooboffs = 0;
+	ops.datbuf = NULL;
+	ops.mode = MTD_OPS_PLACE_OOB;
+
+	if (!rcode)
+		rcode = 0xff;
+	numblocks = (int)(mtd->size >> this->block_shift);
+	nrchips = 1;
+
+	/* Loop through the chips */
+	for (; chip < nrchips; chip++) {
+		/*
+		 * There was already a version of the table, reuse the page
+		 * This applies for absolute placement too, as we have the
+		 * page nr. in td->pages.
+		 */
+		if (td->pages[chip] != -1) {
+			page = td->pages[chip];
+			goto write;
+		}
+
+		/*
+		 * Automatic placement of the bad block table. Search direction
+		 * top -> down?
+		 */
+		if (td->options & NAND_BBT_LASTBLOCK) {
+			startblock = numblocks * (chip + 1) - 1;
+			dir = -1;
+		} else {
+			startblock = chip * numblocks;
+			dir = 1;
+		}
+
+		for (i = 0; i < td->maxblocks; i++) {
+			int block = startblock + dir * i;
+			/* Check, if the block is bad */
+			switch (bbt_get_entry(this, block)) {
+			case BBT_BLOCK_WORN:
+			case BBT_BLOCK_FACTORY_BAD:
+				continue;
+			}
+			page = block <<
+				(this->block_shift - this->page_shift);
+			/* Check, if the block is used by the mirror table */
+			if (!md || md->pages[chip] != page)
+				goto write;
+		}
+		pr_err("No space left to write bad block table\n");
+		return -ENOSPC;
+write:
+
+		/* Set up shift count and masks for the flash table */
+		bits = td->options & NAND_BBT_NRBITS_MSK;
+		msk[2] = ~rcode;
+		switch (bits) {
+		case 1:
+			sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
+			msk[3] = 0x01;
+			break;
+		case 2:
+			sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
+			msk[3] = 0x03;
+			break;
+		case 4:
+			sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
+			msk[3] = 0x0f;
+			break;
+		case 8:
+			sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
+			msk[3] = 0xff;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		to = ((loff_t)page) << this->page_shift;
+
+		/* Must we save the block contents? */
+		if (td->options & NAND_BBT_SAVECONTENT) {
+			/* Make it block aligned */
+			to &= ~((loff_t)((1 << this->block_shift) - 1));
+			len = 1 << this->block_shift;
+			res = mtd_read(mtd, to, len, &retlen, buf);
+			if (res < 0) {
+				if (retlen != len) {
+					pr_info("spi_nand_bbt: error reading block ");
+					pr_info("for writing the bad block table\n");
+					return res;
+				}
+				pr_warn("spi_nand_bbt: ECC error while reading ");
+				pr_warn("block for writing bad block table\n");
+			}
+			/* Read oob data */
+			ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
+			ops.oobbuf = &buf[len];
+			res = mtd_read_oob(mtd, to + mtd->writesize, &ops);
+			if (res < 0 || ops.oobretlen != ops.ooblen)
+				goto outerr;
+
+			/* Calc the byte offset in the buffer */
+			pageoffs = page - (int)(to >> this->page_shift);
+			offs = pageoffs << this->page_shift;
+			/* Preset the bbt area with 0xff */
+			memset(&buf[offs], 0xff, (size_t)(numblocks >> sft));
+			ooboffs = len + (pageoffs * mtd->oobsize);
+
+		} else if (td->options & NAND_BBT_NO_OOB) {
+			ooboffs = 0;
+			offs = td->len;
+			/* The version byte */
+			if (td->options & NAND_BBT_VERSION)
+				offs++;
+			/* Calc length */
+			len = (size_t)(numblocks >> sft);
+			len += offs;
+			/* Make it page aligned! */
+			len = ALIGN(len, mtd->writesize);
+			/* Preset the buffer with 0xff */
+			memset(buf, 0xff, len);
+			/* Pattern is located at the begin of first page */
+			memcpy(buf, td->pattern, td->len);
+		} else {
+			/* Calc length */
+			len = (size_t)(numblocks >> sft);
+			/* Make it page aligned! */
+			len = ALIGN(len, mtd->writesize);
+			/* Preset the buffer with 0xff */
+			memset(buf, 0xff, len +
+			       (len >> this->page_shift) * mtd->oobsize);
+			offs = 0;
+			ooboffs = len;
+			/* Pattern is located in oob area of first page */
+			memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
+		}
+
+		if (td->options & NAND_BBT_VERSION)
+			buf[ooboffs + td->veroffs] = td->version[chip];
+
+		/* Walk through the memory table */
+		for (i = 0; i < numblocks; i++) {
+			uint8_t dat;
+			int sftcnt = (i << (3 - sft)) & sftmsk;
+
+			dat = bbt_get_entry(this, chip * numblocks + i);
+			/* Do not store the reserved bbt blocks! */
+			buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt);
+		}
+
+		memset(&einfo, 0, sizeof(einfo));
+		einfo.mtd = mtd;
+		einfo.addr = to;
+		einfo.len = 1 << this->block_shift;
+		res = __spi_nand_erase(mtd, &einfo, 1);
+		if (res < 0)
+			goto outerr;
+
+		res = scan_write_bbt(mtd, to, len, buf,
+				td->options & NAND_BBT_NO_OOB ? NULL :
+				&buf[len]);
+		if (res < 0)
+			goto outerr;
+
+		pr_info("Bad block table written to 0x%012llx, version 0x%02X\n",
+			 (unsigned long long)to, td->version[chip]);
+
+		/* Mark it as used */
+		td->pages[chip] = page;
+	}
+	return 0;
+
+ outerr:
+	pr_warn("spi_nand_bbt: error while writing bad block table %d\n", res);
+	return res;
+}
+
+/**
+ * spi_nand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device for
+ * manufacturer / software marked good / bad blocks.
+ */
+static inline int spi_nand_memory_bbt(struct mtd_info *mtd,
+				struct nand_bbt_descr *bd)
+{
+	struct spi_nand_chip *this = mtd->priv;
+
+	return create_bbt(mtd, this->buf, bd, -1);
+}
+
+/**
+ * check_create - [GENERIC] create and write bbt(s) if necessary
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function checks the results of the previous call to read_bbt and creates
+ * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found
+ * for the chip/device. Update is necessary if one of the tables is missing or
+ * the version nr. of one table is less than the other.
+ */
+static int check_create(struct mtd_info *mtd, uint8_t *buf,
+			struct nand_bbt_descr *bd)
+{
+	int i, chips, writeops, create, chipsel, res, res2;
+	struct spi_nand_chip *this = mtd->priv;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+	struct nand_bbt_descr *rd, *rd2;
+
+	chips = 1;
+
+	for (i = 0; i < chips; i++) {
+		writeops = 0;
+		create = 0;
+		rd = NULL;
+		rd2 = NULL;
+		res = res2 = 0;
+		/* Per chip or per device? */
+		chipsel = -1;
+		/* Mirrored table available? */
+		if (md) {
+			if (td->pages[i] == -1 && md->pages[i] == -1) {
+				create = 1;
+				writeops = 0x03;
+			} else if (td->pages[i] == -1) {
+				rd = md;
+				writeops = 0x01;
+			} else if (md->pages[i] == -1) {
+				rd = td;
+				writeops = 0x02;
+			} else if (td->version[i] == md->version[i]) {
+				rd = td;
+				if (!(td->options & NAND_BBT_VERSION))
+					rd2 = md;
+			} else if (((int8_t)(td->version[i] - md->version[i]))
+					> 0) {
+				rd = td;
+				writeops = 0x02;
+			} else {
+				rd = md;
+				writeops = 0x01;
+			}
+		} else {
+			if (td->pages[i] == -1) {
+				create = 1;
+				writeops = 0x01;
+			} else {
+				rd = td;
+			}
+		}
+
+		if (create) {
+			/* Create the bad block table by scanning the device? */
+			if (!(td->options & NAND_BBT_CREATE))
+				continue;
+
+			/* Create the table in memory by scanning the chip(s) */
+			if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
+				create_bbt(mtd, buf, bd, chipsel);
+
+			td->version[i] = 1;
+			if (md)
+				md->version[i] = 1;
+		}
+
+		/* Read back first? */
+		if (rd) {
+			res = read_abs_bbt(mtd, buf, rd, chipsel);
+			if (mtd_is_eccerr(res)) {
+				/* Mark table as invalid */
+				rd->pages[i] = -1;
+				rd->version[i] = 0;
+				i--;
+				continue;
+			}
+		}
+		/* If they weren't versioned, read both */
+		if (rd2) {
+			res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
+			if (mtd_is_eccerr(res2)) {
+				/* Mark table as invalid */
+				rd2->pages[i] = -1;
+				rd2->version[i] = 0;
+				i--;
+				continue;
+			}
+		}
+
+		/* Scrub the flash table(s)? */
+		if (mtd_is_bitflip(res) || mtd_is_bitflip(res2))
+			writeops = 0x03;
+
+		/* Update version numbers before writing */
+		if (md) {
+			td->version[i] = max(td->version[i], md->version[i]);
+			md->version[i] = td->version[i];
+		}
+
+		/* Write the bad block table to the device? */
+		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+			res = write_bbt(mtd, buf, td, md, chipsel);
+			if (res < 0)
+				return res;
+		}
+
+		/* Write the mirror bad block table to the device? */
+		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+			res = write_bbt(mtd, buf, md, td, chipsel);
+			if (res < 0)
+				return res;
+		}
+	}
+	return 0;
+}
+
+/**
+ * mark_bbt_regions - [GENERIC] mark the bad block table regions
+ * @mtd: MTD device structure
+ * @td: bad block table descriptor
+ *
+ * The bad block table regions are marked as "bad" to prevent accidental
+ * erasures / writes. The regions are identified by the mark 0x02.
+ */
+static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int i, j, chips, block, nrblocks, update;
+	uint8_t oldval;
+
+	chips = 1;
+	nrblocks = (int)(mtd->size >> this->block_shift);
+
+	for (i = 0; i < chips; i++) {
+		if ((td->options & NAND_BBT_ABSPAGE) ||
+		    !(td->options & NAND_BBT_WRITE)) {
+			if (td->pages[i] == -1)
+				continue;
+			block = td->pages[i] >>
+				(this->block_shift - this->page_shift);
+			oldval = bbt_get_entry(this, block);
+			bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
+			if ((oldval != BBT_BLOCK_RESERVED) &&
+					td->reserved_block_code)
+				spi_nand_update_bbt(mtd, (loff_t)block <<
+						this->block_shift);
+			continue;
+		}
+		update = 0;
+		if (td->options & NAND_BBT_LASTBLOCK)
+			block = ((i + 1) * nrblocks) - td->maxblocks;
+		else
+			block = i * nrblocks;
+		for (j = 0; j < td->maxblocks; j++) {
+			oldval = bbt_get_entry(this, block);
+			bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
+			if (oldval != BBT_BLOCK_RESERVED)
+				update = 1;
+			block++;
+		}
+		/*
+		 * If we want reserved blocks to be recorded to flash, and some
+		 * new ones have been marked, then we need to update the stored
+		 * bbts.  This should only happen once.
+		 */
+		if (update && td->reserved_block_code)
+			spi_nand_update_bbt(mtd, (loff_t)(block - 1) <<
+					this->block_shift);
+	}
+}
+
+/**
+ * verify_bbt_descr - verify the bad block description
+ * @mtd: MTD device structure
+ * @bd: the table to verify
+ *
+ * This functions performs a few sanity checks on the bad block description
+ * table.
+ */
+static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	u32 pattern_len;
+	u32 bits;
+	u32 table_size;
+
+	if (!bd)
+		return;
+
+	pattern_len = bd->len;
+	bits = bd->options & NAND_BBT_NRBITS_MSK;
+
+	BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) &&
+			!(this->bbt_options & NAND_BBT_USE_FLASH));
+	BUG_ON(!bits);
+
+	if (bd->options & NAND_BBT_VERSION)
+		pattern_len++;
+
+	if (bd->options & NAND_BBT_NO_OOB) {
+		BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH));
+		BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB));
+		BUG_ON(bd->offs);
+		if (bd->options & NAND_BBT_VERSION)
+			BUG_ON(bd->veroffs != bd->len);
+		BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
+	}
+
+	table_size = mtd->size >> this->block_shift;
+	table_size >>= 3;
+	table_size *= bits;
+	if (bd->options & NAND_BBT_NO_OOB)
+		table_size += pattern_len;
+	BUG_ON(table_size > (1 << this->block_shift));
+}
+
+/**
+ * spi_nand_scan_bbt - [SPI-NAND Interface] scan, find, read and maybe create
+ * bad block table(s)
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already available. If
+ * not it scans the device for manufacturer marked good / bad blocks and writes
+ * the bad block table(s) to the selected place.
+ *
+ * The bad block table memory is allocated here. It must be freed by calling
+ * the spi_nand_free_bbt function.
+ */
+int spi_nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int len, res = 0;
+	uint8_t *buf;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+
+	len = mtd->size >> (this->block_shift + 2);
+	/*
+	 * Allocate memory (2bit per block) and clear the memory bad block
+	 * table.
+	 */
+	this->bbt = kzalloc(len, GFP_KERNEL);
+	if (!this->bbt)
+		return -ENOMEM;
+
+	/*
+	 * If no primary table decriptor is given, scan the device to build a
+	 * memory based bad block table.
+	 */
+	if (!td) {
+		res = spi_nand_memory_bbt(mtd, bd);
+		if (res) {
+			pr_err("spi_nand_bbt: can't scan flash and build the RAM-based BBT\n");
+			kfree(this->bbt);
+			this->bbt = NULL;
+		}
+		return res;
+	}
+	verify_bbt_descr(mtd, td);
+	verify_bbt_descr(mtd, md);
+
+	/* Allocate a temporary buffer for one eraseblock incl. oob */
+	len = (1 << this->block_shift);
+	len += (len >> this->page_shift) * mtd->oobsize;
+	buf = vmalloc(len);
+	if (!buf) {
+		kfree(this->bbt);
+		this->bbt = NULL;
+		return -ENOMEM;
+	}
+
+	/* Is the bbt at a given page? */
+	if (td->options & NAND_BBT_ABSPAGE) {
+		read_abs_bbts(mtd, buf, td, md);
+	} else {
+		/* Search the bad block table using a pattern in oob */
+		search_read_bbts(mtd, buf, td, md);
+	}
+
+	res = check_create(mtd, buf, bd);
+
+	/* Prevent the bbt regions from erasing / writing */
+	mark_bbt_region(mtd, td);
+	if (md)
+		mark_bbt_region(mtd, md);
+
+	vfree(buf);
+	return res;
+}
+EXPORT_SYMBOL(spi_nand_scan_bbt);
+
+/**
+ * spi_nand_update_bbt - update bad block table(s)
+ * @mtd: MTD device structure
+ * @offs: the offset of the newly marked block
+ *
+ * The function updates the bad block table(s).
+ */
+static int spi_nand_update_bbt(struct mtd_info *mtd, loff_t offs)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int len, res = 0;
+	int chip, chipsel;
+	uint8_t *buf;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+
+	if (!this->bbt || !td)
+		return -EINVAL;
+
+	/* Allocate a temporary buffer for one eraseblock incl. oob */
+	len = (1 << this->block_shift);
+	len += (len >> this->page_shift) * mtd->oobsize;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	chip = 0;
+	chipsel = -1;
+
+	td->version[chip]++;
+	if (md)
+		md->version[chip]++;
+
+	/* Write the bad block table to the device? */
+	if (td->options & NAND_BBT_WRITE) {
+		res = write_bbt(mtd, buf, td, md, chipsel);
+		if (res < 0)
+			goto out;
+	}
+	/* Write the mirror bad block table to the device? */
+	if (md && (md->options & NAND_BBT_WRITE))
+		res = write_bbt(mtd, buf, md, td, chipsel);
+
+ out:
+	kfree(buf);
+	return res;
+}
+
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+/* Generic flash bbt descriptors */
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+	.pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+	.pattern = mirror_pattern
+};
+
+static struct nand_bbt_descr bbt_main_no_oob_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_NO_OOB,
+	.len = 4,
+	.veroffs = 4,
+	.maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+	.pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_no_oob_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_NO_OOB,
+	.len = 4,
+	.veroffs = 4,
+	.maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+	.pattern = mirror_pattern
+};
+
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
+/**
+ * spi_nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
+ * @this: SPI-NAND chip to create descriptor for
+ *
+ * This function allocates and initializes a nand_bbt_descr for BBM detection
+ * based on the properties of @this. The new descriptor is stored in
+ * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when
+ * passed to this function.
+ */
+static int spi_nand_create_badblock_pattern(struct spi_nand_chip *this)
+{
+	struct nand_bbt_descr *bd;
+
+	if (this->badblock_pattern) {
+		pr_warn("Bad block pattern already allocated; not replacing\n");
+		return -EINVAL;
+	}
+	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
+	if (!bd)
+		return -ENOMEM;
+	bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;
+	bd->offs = this->badblockpos;
+	bd->len = 1;
+	bd->pattern = scan_ff_pattern;
+	bd->options |= NAND_BBT_DYNAMICSTRUCT;
+	this->badblock_pattern = bd;
+	return 0;
+}
+
+/**
+ * spi_nand_default_bbt - [SPI-NAND Interface] Select a default bad block table for the device
+ * @mtd: MTD device structure
+ *
+ * This function selects the default bad block table support for the device and
+ * calls the spi_nand_scan_bbt function.
+ */
+int spi_nand_default_bbt(struct mtd_info *mtd)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int ret;
+
+	/* Is a flash based bad block table requested? */
+	if (this->bbt_options & NAND_BBT_USE_FLASH) {
+		/* Use the default pattern descriptors */
+		if (!this->bbt_td) {
+			if (this->bbt_options & NAND_BBT_NO_OOB) {
+				this->bbt_td = &bbt_main_no_oob_descr;
+				this->bbt_md = &bbt_mirror_no_oob_descr;
+			} else {
+				this->bbt_td = &bbt_main_descr;
+				this->bbt_md = &bbt_mirror_descr;
+			}
+		}
+	} else {
+		this->bbt_td = NULL;
+		this->bbt_md = NULL;
+	}
+
+	if (!this->badblock_pattern) {
+		ret = spi_nand_create_badblock_pattern(this);
+		if (ret)
+			return ret;
+	}
+
+	return spi_nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+/**
+ * spi_nand_isbad_bbt - [SPI-NAND Interface] Check if a block is bad
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ * @allowbbt: allow access to bad block table region
+ */
+int spi_nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int block, res;
+
+	block = (int)(offs >> this->block_shift);
+	res = bbt_get_entry(this, block);
+
+	pr_debug("%s: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+			__func__, (unsigned int)offs, block, res);
+
+	switch (res) {
+	case BBT_BLOCK_GOOD:
+		return 0;
+	case BBT_BLOCK_WORN:
+		return 1;
+	case BBT_BLOCK_RESERVED:
+		return allowbbt ? 0 : 1;
+	}
+	return 1;
+}
+
+/**
+ * spi_nand_markbad_bbt - [SPI-NAND Interface] Mark a block bad in the BBT
+ * @mtd: MTD device structure
+ * @offs: offset of the bad block
+ */
+int spi_nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
+{
+	struct spi_nand_chip *this = mtd->priv;
+	int block, ret = 0;
+
+	block = (int)(offs >> this->block_shift);
+
+	/* Mark bad block in memory */
+	bbt_mark_entry(this, block, BBT_BLOCK_WORN);
+
+	/* Update flash-based bad block table */
+	if (this->bbt_options & NAND_BBT_USE_FLASH)
+		ret = spi_nand_update_bbt(mtd, offs);
+
+	return ret;
+}
diff --git a/include/linux/mtd/spi-nand.h b/include/linux/mtd/spi-nand.h
new file mode 100644
index 0000000..e29fd5d
--- /dev/null
+++ b/include/linux/mtd/spi-nand.h
@@ -0,0 +1,317 @@ 
+/*-
+ *
+ * Copyright (c) 2009-2014 Micron Technology, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * Peter Pan <peterpandong@micron.com>
+ *
+ * based on mt29f_spinand.h
+ */
+#ifndef __LINUX_MTD_SPI_NAND_H
+#define __LINUX_MTD_SPI_NAND_H
+
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/flashchip.h>
+
+
+/*
+ * Standard SPI-NAND flash commands
+ */
+#define SPINAND_CMD_READ			0x13
+#define SPINAND_CMD_READ_RDM			0x03
+#define SPINAND_CMD_PROG_PAGE_CLRCACHE		0x02
+#define SPINAND_CMD_PROG_PAGE			0x84
+#define SPINAND_CMD_PROG_PAGE_EXC		0x10
+#define SPINAND_CMD_ERASE_BLK			0xd8
+#define SPINAND_CMD_WR_ENABLE			0x06
+#define SPINAND_CMD_WR_DISABLE			0x04
+#define SPINAND_CMD_READ_ID			0x9f
+#define SPINAND_CMD_RESET			0xff
+#define SPINAND_CMD_READ_REG			0x0f
+#define SPINAND_CMD_WRITE_REG			0x1f
+#define SPINAND_CMD_READ_CACHE_X2		0x3b
+#define SPINAND_CMD_READ_CACHE_X4		0x6b
+
+/* feature registers */
+#define REG_BLOCK_LOCK			0xa0
+#define REG_OTP				0xb0
+#define REG_STATUS			0xc0/* timing */
+
+/* status */
+#define STATUS_OIP_MASK			0x01
+#define STATUS_READY			(0 << 0)
+#define STATUS_BUSY			(1 << 0)
+
+#define STATUS_E_FAIL_MASK		0x04
+#define STATUS_E_FAIL			(1 << 2)
+
+#define STATUS_P_FAIL_MASK		0x08
+#define STATUS_P_FAIL			(1 << 3)
+
+
+/*OTP register defines*/
+#define OTP_ECC_MASK			0X10
+#define OTP_ECC_ENABLE			0x10
+#define OTP_ENABLE			0x40
+
+
+/* block lock */
+#define BL_ALL_LOCKED      0x38
+#define BL_1_2_LOCKED      0x30
+#define BL_1_4_LOCKED      0x28
+#define BL_1_8_LOCKED      0x20
+#define BL_1_16_LOCKED     0x18
+#define BL_1_32_LOCKED     0x10
+#define BL_1_64_LOCKED     0x08
+#define BL_ALL_UNLOCKED    0
+
+
+
+#define SPI_NAND_MT29F_ECC_MASK		3
+#define SPI_NAND_MT29F_ECC_CORRECTED	1
+#define SPI_NAND_MT29F_ECC_UNCORR	2
+#define SPI_NAND_MT29F_ECC_RESERVED	3
+#define SPI_NAND_MT29F_ECC_SHIFT	4
+
+
+#define SPI_NAND_GD5F_ECC_MASK		7
+#define SPI_NAND_GD5F_ECC_UNCORR	7
+#define SPI_NAND_GD5F_ECC_SHIFT		4
+
+
+
+
+struct spi_nand_onfi_params {
+	/* rev info and features block */
+	/* 'O' 'N' 'F' 'I'  */
+	u8		sig[4];				/*0-3*/
+	__le16		revision;			/*4-5*/
+	__le16		features;			/*6-7*/
+	__le16		opt_cmd;			/*8-9*/
+	u8		reserved0[22];			/*10-31*/
+
+	/* manufacturer information block */
+	char		manufacturer[12];		/*32-43*/
+	char		model[20];			/*44-63*/
+	u8		mfr_id;				/*64*/
+	__le16		date_code;			/*65-66*/
+	u8		reserved1[13];			/*67-79*/
+
+	/* memory organization block */
+	__le32		byte_per_page;			/*80-83*/
+	__le16		spare_bytes_per_page;		/*84*85*/
+	__le32		data_bytes_per_ppage;		/*86-89*/
+	__le16		spare_bytes_per_ppage;		/*90-91*/
+	__le32		pages_per_block;		/*92-95*/
+	__le32		blocks_per_lun;			/*96-99*/
+	u8		lun_count;			/*100*/
+	u8		addr_cycles;			/*101*/
+	u8		bits_per_cell;			/*102*/
+	__le16		bb_per_lun;			/*103-104*/
+	__le16		block_endurance;		/*105-106*/
+	u8		guaranteed_good_blocks;		/*107*/
+	__le16		guaranteed_block_endurance;	/*108-109*/
+	u8		programs_per_page;		/*110*/
+	u8		ppage_attr;			/*111*/
+	u8		ecc_bits;			/*112*/
+	u8		interleaved_bits;		/*113*/
+	u8		interleaved_ops;		/*114*/
+	u8		reserved2[13];			/*115-127*/
+
+	/* electrical parameter block */
+	u8		io_pin_capacitance_max;		/*128*/
+	__le16		timing_mode;			/*129-130*/
+	__le16		program_cache_timing_mode;	/*131-132*/
+	__le16		t_prog;				/*133-134*/
+	__le16		t_bers;				/*135-136*/
+	__le16		t_r;				/*137-138*/
+	__le16		t_ccs;				/*139-140*/
+	u8		reserved3[23];			/*141-163*/
+
+	/* vendor */
+	__le16		vendor_specific_revision;	/*164-165*/
+	u8		vendor_specific[88];		/*166-253*/
+
+	__le16		crc;				/*254-255*/
+} __packed;
+
+#define ONFI_CRC_BASE	0x4F4E
+
+/**
+ * struct spi_nand_chip - SPI-NAND Private Flash Chip Data
+ * @chip_lock:		[INTERN] protection lock
+ * @name:		name of the chip
+ * @wq:			[INTERN] wait queue to sleep on if a SPI-NAND operation
+ *			is in progress used instead of the per chip wait queue
+ *			when a hw controller is available.
+ * @mfr_id:		[BOARDSPECIFIC] manufacture id
+ * @dev_id:		[BOARDSPECIFIC] device id
+ * @state:		[INTERN] the current state of the SPI-NAND device
+ * @spi:		[INTERN] point to spi device structure
+ * @mtd:		[INTERN] point to MTD device structure
+ * @reset:		[REPLACEABLE] function to reset the device
+ * @read_id:		[REPLACEABLE] read manufacture id and device id
+ * @load_page:		[REPLACEABLE] load page from NAND to cache
+ * @read_cache:		[REPLACEABLE] read data from cache
+ * @store_cache:	[REPLACEABLE] write data to cache
+ * @write_page:		[REPLACEABLE] program NAND with cache data
+ * @erase_block:	[REPLACEABLE] erase a given block
+ * @waitfunc:		[REPLACEABLE] wait for ready.
+ * @write_enable:	[REPLACEABLE] set write enable latch
+ * @get_ecc_status:	[REPLACEABLE] get ecc and bitflip status
+ * @enable_ecc:		[REPLACEABLE] enable on-die ecc
+ * @disable_ecc:	[REPLACEABLE] disable on-die ecc
+ * @buf:		[INTERN] buffer for read/write
+ * @oobbuf:		[INTERN] buffer for read/write oob
+ * @pagebuf:		[INTERN] holds the pagenumber which is currently in
+ *			data_buf.
+ * @pagebuf_bitflips:	[INTERN] holds the bitflip count for the page which is
+ *			currently in data_buf.
+ * @size:		[INTERN] the size of chip
+ * @block_size:		[INTERN] the size of eraseblock
+ * @page_size:		[INTERN] the size of page
+ * @page_spare_size:	[INTERN] the size of page oob size
+ * @block_shift:	[INTERN] number of address bits in a eraseblock
+ * @page_shift:		[INTERN] number of address bits in a page (column
+ *			address bits).
+ * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
+ * @options:		[BOARDSPECIFIC] various chip options. They can partly
+ *			be set to inform nand_scan about special functionality.
+ * @ecc_strength_ds:	[INTERN] ECC correctability from the datasheet.
+ *			Minimum amount of bit errors per @ecc_step_ds guaranteed
+ *			to be correctable. If unknown, set to zero.
+ * @ecc_step_ds:	[INTERN] ECC step required by the @ecc_strength_ds,
+ *                      also from the datasheet. It is the recommended ECC step
+ *			size, if known; if unknown, set to zero.
+ * @bits_per_cell:	[INTERN] number of bits per cell. i.e., 1 means SLC.
+ * @ecclayout:		[BOARDSPECIFIC] ECC layout control structure
+ *			See the defines for further explanation.
+ * @bbt_options:	[INTERN] bad block specific options. All options used
+ *			here must come from bbm.h. By default, these options
+ *			will be copied to the appropriate nand_bbt_descr's.
+ * @bbt:		[INTERN] bad block table pointer
+ * @badblockpos:	[INTERN] position of the bad block marker in the oob
+ *			area.
+ * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash
+ *			lookup.
+ * @bbt_md:		[REPLACEABLE] bad block table mirror descriptor
+ * @badblock_pattern:	[REPLACEABLE] bad block scan pattern used for initial
+ *			bad block scan.
+ * @onfi_params:	[INTERN] holds the ONFI page parameter when ONFI is
+ *			supported, 0 otherwise.
+ */
+
+struct spi_nand_chip {
+	spinlock_t	chip_lock;
+	char		*name;
+	wait_queue_head_t wq;
+	u8		mfr_id;
+	u8		dev_id;
+	flstate_t	state;
+	struct spi_device	*spi;
+	struct mtd_info	*mtd;
+
+	int (*reset)(struct spi_nand_chip *chip);
+	int (*read_id)(struct spi_nand_chip *chip, u8 *id);
+	int (*load_page)(struct spi_nand_chip *chip, unsigned int page_addr);
+	int (*read_cache)(struct spi_nand_chip *chip, unsigned int page_addr,
+		unsigned int page_offset,	size_t length, u8 *read_buf);
+	int (*store_cache)(struct spi_nand_chip *chip, unsigned int page_addr,
+		unsigned int page_offset,	size_t length, u8 *write_buf);
+	int (*write_page)(struct spi_nand_chip *chip, unsigned int page_addr);
+	int (*erase_block)(struct spi_nand_chip *chip, u32 page_addr);
+	int (*waitfunc)(struct spi_nand_chip *chip, u8 *status);
+	int (*write_enable)(struct spi_nand_chip *chip);
+	void (*get_ecc_status)(unsigned int status,
+						unsigned int *corrected,
+						unsigned int *ecc_errors);
+	int (*enable_ecc)(struct spi_nand_chip *chip);
+	int (*disable_ecc)(struct spi_nand_chip *chip);
+
+	u8		*buf;
+	u8		*oobbuf;
+	int		pagebuf;
+	u32		pagebuf_bitflips;
+	u64		size;
+	u32		block_size;
+	u16		page_size;
+	u16		page_spare_size;
+	u8		block_shift;
+	u8		page_shift;
+	u16		page_mask;
+	u32		options;
+	u16		ecc_strength_ds;
+	u16		ecc_step_ds;
+	u8		bits_per_cell;
+	struct nand_ecclayout *ecclayout;
+	u32		bbt_options;
+	u8		*bbt;
+	int		badblockpos;
+	struct nand_bbt_descr *bbt_td;
+	struct nand_bbt_descr *bbt_md;
+	struct nand_bbt_descr *badblock_pattern;
+	struct spi_nand_onfi_params	 onfi_params;
+};
+
+struct spi_nand_flash {
+	char		*name;
+	u8		mfr_id;
+	u8		dev_id;
+	u32		page_size;
+	u32		page_spare_size;
+	u32		pages_per_blk;
+	u32		blks_per_chip;
+	u32		options;
+};
+
+struct spi_nand_cmd {
+	u8		cmd;
+	u32		n_addr;		/* Number of address */
+	u8		addr[3];	/* Reg Offset */
+	u32		n_tx;		/* Number of tx bytes */
+	u8		*tx_buf;	/* Tx buf */
+	u8		tx_nbits;
+	u32		n_rx;		/* Number of rx bytes */
+	u8		*rx_buf;	/* Rx buf */
+	u8		rx_nbits;
+};
+
+#define SPI_NAND_INFO(nm, mid, did, pagesz, sparesz, pg_per_blk,\
+	blk_per_chip, opts)				\
+	{ .name = (nm), .mfr_id = (mid), .dev_id = (did),\
+	.page_size = (pagesz), .page_spare_size = (sparesz),\
+	.pages_per_blk = (pg_per_blk), .blks_per_chip = (blk_per_chip),\
+	.options = (opts) }
+
+#define SPINAND_NEED_PLANE_SELECT	(1 << 0)
+
+#define SPINAND_MFR_MICRON		0x2C
+#define SPINAND_MFR_GIGADEVICE	0xC8
+#define SPINAND_MAX_ID_LEN		2
+
+int spi_nand_send_cmd(struct spi_device *spi, struct spi_nand_cmd *cmd);
+int spi_nand_read_from_cache(struct spi_nand_chip *chip,
+		u32 page_addr, u32 column, size_t len, u8 *rbuf);
+int spi_nand_read_from_cache_snor_protocol(struct spi_nand_chip *chip,
+		u32 page_addr, u32 column, size_t len, u8 *rbuf);
+int spi_nand_scan_ident(struct mtd_info *mtd);
+int spi_nand_scan_tail(struct mtd_info *mtd);
+int spi_nand_scan_ident_release(struct mtd_info *mtd);
+int spi_nand_scan_tail_release(struct mtd_info *mtd);
+int spi_nand_release(struct mtd_info *mtd);
+int __spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo,
+		int allowbbt);
+int spi_nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
+int spi_nand_default_bbt(struct mtd_info *mtd);
+int spi_nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
+#endif /* __LINUX_MTD_SPI_NAND_H */