[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <87F60714EC601C4C83DFF1D2E3D390A04AB404@NTXXIAMBX02.xacn.micron.com>
Date: Thu, 8 Jan 2015 00:48:49 +0000
From: Peter Pan 潘栋 (peterpandong)
<peterpandong@...ron.com>
To: "dwmw2@...radead.org" <dwmw2@...radead.org>,
Brian Norris <computersforpeace@...il.com>,
Ezequiel Garcia <ezequiel.garcia@...tec.com>
CC: "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
"linux-mtd@...ts.infradead.org" <linux-mtd@...ts.infradead.org>,
Qi Wang 王起 (qiwang) <qiwang@...ron.com>,
Frank Liu 刘群 (frankliu)
<frankliu@...ron.com>,
Melanie Zhang 张燕 (melaniezhang)
<melaniezhang@...ron.com>,
Peter Pan 潘栋 (peterpandong)
<peterpandong@...ron.com>
Subject: [PATCH 1/3] mtd: spi-nand framework
Add framework to support spi nand devices. The code is derived from
parallel nand code.
Signed-off-by: Peter Pan <peterpandong@...ron.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
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@...ron.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@...ron.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 */
--
1.9.1
Powered by blists - more mailing lists