[PATCH 09/11] nand: spi: Add BBT support

Peter Pan peterpandong at micron.com
Tue Feb 21 00:00:08 PST 2017


This commit enables BBT for SPI NAND. We also define
SPINAND_SKIP_BBTSCAN to skip to use BBT for some
chipset vendors' request.

Signed-off-by: Peter Pan <peterpandong at micron.com>
---
 drivers/mtd/nand/spi/spi-nand-base.c | 195 +++++++++++++++++++++++++++++++----
 include/linux/mtd/spi-nand.h         |   1 +
 2 files changed, 176 insertions(+), 20 deletions(-)

diff --git a/drivers/mtd/nand/spi/spi-nand-base.c b/drivers/mtd/nand/spi/spi-nand-base.c
index 57986d5..5ac4b26 100644
--- a/drivers/mtd/nand/spi/spi-nand-base.c
+++ b/drivers/mtd/nand/spi/spi-nand-base.c
@@ -960,6 +960,27 @@ static int spi_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 	return ret;
 }
 
+/**
+ * spi_nand_block_checkbad - Check if a block is marked bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @getchip: 0, if the chip is already selected
+ * @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 getchip, int allowbbt)
+{
+	struct nand_device *nand = mtd_to_nand(mtd);
+
+	if (!nand->bbt.bbt)
+		return spi_nand_block_bad(mtd, ofs, getchip);
+
+	/* Return info from the table */
+	return nand_isbad_bbt(nand, ofs, allowbbt);
+}
 
 /**
  * spi_nand_block_isbad - [MTD Interface] Check if block at offset is bad
@@ -968,7 +989,7 @@ static int spi_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
  */
 static int spi_nand_block_isbad(struct mtd_info *mtd, loff_t offs)
 {
-	return spi_nand_block_bad(mtd, offs, 1);
+	return spi_nand_block_checkbad(mtd, offs, 1, 0);
 }
 
 /**
@@ -995,22 +1016,31 @@ static int spi_nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
 	struct erase_info einfo = {0};
 	u32 block_addr;
 	u8 buf[2] = {0, 0};
-	int ret = 0;
-
-	/*erase bad block before mark bad block*/
-	einfo.mtd = mtd;
-	einfo.addr = ofs;
-	einfo.len = nand_eraseblock_size(nand);
-	spi_nand_erase(mtd, &einfo);
+	int res, ret = 0;
+
+	if (!nand->bbt.bbt || !(nand->bbt.options & NAND_BBT_NO_OOB_BBM)) {
+		/*erase bad block before mark bad block*/
+		einfo.mtd = mtd;
+		einfo.addr = ofs;
+		einfo.len = nand_eraseblock_size(nand);
+		spi_nand_erase(mtd, &einfo);
+
+		block_addr = nand_offs_to_eraseblock(nand, ofs);
+		ops.mode = MTD_OPS_PLACE_OOB;
+		ops.ooblen = 2;
+		ops.oobbuf = buf;
+		spi_nand_get_device(mtd, FL_WRITING);
+		ret = spi_nand_do_write_ops(mtd,
+			nand_eraseblock_to_offs(nand, block_addr), &ops);
+		spi_nand_release_device(mtd);
+	}
 
-	block_addr = nand_offs_to_eraseblock(nand, ofs);
-	ops.mode = MTD_OPS_PLACE_OOB;
-	ops.ooblen = 2;
-	ops.oobbuf = buf;
-	spi_nand_get_device(mtd, FL_WRITING);
-	ret = spi_nand_do_write_ops(mtd,
-		nand_eraseblock_to_offs(nand, block_addr), &ops);
-	spi_nand_release_device(mtd);
+	/* Mark block bad in BBT */
+	if (nand->bbt.bbt) {
+		res = nand_markbad_bbt(nand, ofs);
+		if (!ret)
+			ret = res;
+	}
 
 	if (!ret)
 		mtd->ecc_stats.badblocks++;
@@ -1040,13 +1070,15 @@ static int spi_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 }
 
 /**
- * spi_nand_erase - [MTD Interface] erase block(s)
+ * __spi_nand_erase - erase block(s)
  * @mtd: MTD device structure
  * @einfo: erase instruction
+ * @allowbbt: allow to access bbt
  *
  * Erase one ore more blocks
  */
-static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo)
+static int __spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo,
+			int allowbbt)
 {
 	struct spi_nand_chip *chip = mtd_to_spi_nand(mtd);
 	struct nand_device *nand = mtd_to_nand(mtd);
@@ -1081,7 +1113,7 @@ static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo)
 
 	while (len) {
 		/* Check if we have a bad block, we do not erase bad blocks! */
-		if (spi_nand_block_bad(mtd, offs, 0)) {
+		if (spi_nand_block_checkbad(mtd, offs, 0, allowbbt)) {
 			pr_warn("%s: attempt to erase a bad block at 0x%012llx\n",
 			__func__, offs);
 			einfo->state = MTD_ERASE_FAILED;
@@ -1124,6 +1156,55 @@ static int spi_nand_erase(struct mtd_info *mtd, struct erase_info *einfo)
 }
 
 /**
+ * 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);
+}
+
+
+static int spi_nand_erase_bbt(struct nand_device *nand, struct erase_info *einfo)
+{
+	return __spi_nand_erase(nand_to_mtd(nand), einfo, 1);
+}
+
+static int spi_nand_markbad(struct nand_device *nand, int block)
+{
+	struct mtd_oob_ops ops = {0};
+	u8 buf[2] = {0, 0};
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.ooboffs = 0;
+	ops.ooblen = 2;
+	ops.oobbuf = buf;
+
+	return spi_nand_do_write_ops(nand_to_mtd(nand),
+		nand_eraseblock_to_offs(nand, block), &ops);
+
+}
+
+/**
+ * spi_nand_block_isreserved - [MTD Interface] Check if a block is
+ * marked reserved.
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ */
+static int spi_nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_device *nand = mtd_to_nand(mtd);
+
+	if (!nand->bbt.bbt)
+		return 0;
+	/* Return info from the table */
+	return nand_isreserved_bbt(nand, ofs);
+}
+
+/**
  * spi_nand_scan_id_table - scan chip info in id table
  * @chip: SPI-NAND device structure
  * @id: point to manufacture id and device id
@@ -1191,6 +1272,74 @@ static void spi_nand_set_rd_wr_op(struct spi_nand_chip *chip)
 		chip->write_cache_op = SPINAND_CMD_PROG_LOAD;
 }
 
+static const struct nand_ops spi_nand_ops = {
+	.erase = spi_nand_erase_bbt,
+	.markbad = spi_nand_markbad,
+};
+
+/*
+ * 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 };
+
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
+
+/**
+ * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
+ * @this: NAND chip to create descriptor for
+ * @chip: SPI-NAND device structure
+ *
+ * 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 *chip)
+{
+	struct nand_device *nand = &chip->base;
+	struct nand_bbt_descr *bd;
+
+	if (nand->bbt.bbp) {
+		pr_warn("Bad block pattern already allocated; not replacing\n");
+		return -EINVAL;
+	}
+	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
+	if (!bd)
+		return -ENOMEM;
+	bd->options = nand->bbt.options & BADBLOCK_SCAN_MASK;
+	bd->offs = 0;
+	bd->len = 2;
+	bd->pattern = scan_ff_pattern;
+	bd->options |= NAND_BBT_DYNAMICSTRUCT;
+	nand->bbt.bbp = bd;
+	return 0;
+}
+
+static int spi_nand_default_bbt(struct mtd_info *mtd)
+{
+	struct nand_device *nand = mtd_to_nand(mtd);
+	struct spi_nand_chip *chip = mtd_to_spi_nand(mtd);
+	int ret;
+
+	nand->bbt.options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+	nand->bbt.td = NULL;
+	nand->bbt.md = NULL;
+
+	ret = spi_nand_create_badblock_pattern(chip);
+	if (ret)
+		return ret;
+
+	return nand_scan_bbt(nand);
+}
+
+static void spi_nand_fill_nandd(struct spi_nand_chip *chip)
+{
+	struct nand_device *nand = &chip->base;
+
+	nand->ops = &spi_nand_ops;
+}
+
 /**
  * spi_nand_scan_ident - [SPI-NAND Interface] Scan for the SPI-NAND device
  * @chip: SPI-NAND device structure
@@ -1232,6 +1381,7 @@ int spi_nand_scan_ident(struct spi_nand_chip *chip)
 		return -ENOMEM;
 
 	chip->oobbuf = chip->buf + nand_page_size(nand);
+	spi_nand_fill_nandd(chip);
 	spi_nand_lock_block(chip, BL_ALL_UNLOCKED);
 
 	return 0;
@@ -1281,11 +1431,16 @@ int spi_nand_scan_tail(struct spi_nand_chip *chip)
 	mtd->_write_oob = spi_nand_write_oob;
 	mtd->_block_isbad = spi_nand_block_isbad;
 	mtd->_block_markbad = spi_nand_block_markbad;
+	mtd->_block_isreserved = spi_nand_block_isreserved;
 
 	if (!mtd->bitflip_threshold)
 		mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
 
-	return 0;
+	/* Check, if we should skip the bad block table scan */
+	if (chip->options & SPINAND_SKIP_BBTSCAN)
+		return 0;
+	/* Build bad block table */
+	return spi_nand_default_bbt(mtd);
 }
 EXPORT_SYMBOL_GPL(spi_nand_scan_tail);
 
diff --git a/include/linux/mtd/spi-nand.h b/include/linux/mtd/spi-nand.h
index cdacec4..5f362b6 100644
--- a/include/linux/mtd/spi-nand.h
+++ b/include/linux/mtd/spi-nand.h
@@ -251,6 +251,7 @@ struct spi_nand_cmd_cfg {
 
 /*SPI NAND chip options*/
 #define SPINAND_NEED_PLANE_SELECT	(1 << 0)
+#define SPINAND_SKIP_BBTSCAN		(1 << 3)
 
 /*SPI NAND manufacture ID definition*/
 #define SPINAND_MFR_MICRON		0x2C
-- 
1.9.1




More information about the linux-mtd mailing list