Add framework to support spi nand devices. The code is derived from parallel nand code.
Signed-off-by: Peter Pan <[email protected]> --- 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<[email protected]>"); +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 <[email protected]> + * + * 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

