[PATCH 6/6] mtd: nand: make new BBT work

Peter Pan 潘栋 (peterpandong) peterpandong at micron.com
Wed Apr 15 19:19:00 PDT 2015


Continue with Brain Norris's work. Allocate and initialize struct
nand_bbt in nand_default_bbt()(nand_base.c). Remove .bbt from nand_chip.
Complete hook nand_is_bad_bbm(nand_bbt.is_bad_bbm). Allocate and initialize
badblock_pattern in nand_chip if badblock_pattern does not exist.
And clean some checkpatch warnings.

TBD: Allocate badblock_pattern is a temporary way to make new BBT work.
Remove .badblock_pattern from struct nand_chip and use .badblockpos,
.bbt_options and .options instead is the next thing to do.

Signed-off-by: Peter Pan <peterpandong at micron.com>
---
 drivers/mtd/nand/diskonchip.c |   3 +-
 drivers/mtd/nand/docg4.c      |   5 ++-
 drivers/mtd/nand/nand_base.c  | 100 ++++++++++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/nand_bbt.c   |  16 ++++---
 include/linux/mtd/nand.h      |   1 -
 include/linux/mtd/nand_bbt.h  |   8 +++-
 6 files changed, 121 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index e580014..b980d77 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -1613,7 +1613,8 @@ static int __init doc_probe(unsigned long physadr)
 	else
 		numchips = doc2001_init(mtd);
 
-	if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) {
+	if ((ret = nand_scan(mtd, numchips))
+		|| (ret = doc->late_init(mtd))) {
 		/* DBB note: i believe nand_release is necessary here, as
 		   buffers may have been allocated in nand_base.  Check with
 		   Thomas. FIX ME! */
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
index 9b671b9..5ed7c7c 100644
--- a/drivers/mtd/nand/docg4.c
+++ b/drivers/mtd/nand/docg4.c
@@ -1037,7 +1037,7 @@ static int __init read_factory_bbt(struct mtd_info *mtd)
 	 * operation after device power-up.  The above read ensures it never is.
 	 * Ugly, I know.
 	 */
-	if (nand->bbt == NULL)  /* no memory-based bbt */
+	if (nand->nand_bbt == NULL)  /* no memory-based bbt */
 		goto exit;
 
 	if (mtd->ecc_stats.failed > eccfailed_stats) {
@@ -1064,7 +1064,8 @@ static int __init read_factory_bbt(struct mtd_info *mtd)
 		unsigned long bits = ~buf[i];
 		for_each_set_bit(bitnum, &bits, 8) {
 			int badblock = block + 7 - bitnum;
-			nand_bbt_markbad(nand->nand_bbt, badblock << nand->bbt_erase_shift);
+			nand_bbt_markbad(nand->nand_bbt, badblock
+					<< nand->bbt_erase_shift);
 			mtd->ecc_stats.badblocks++;
 			dev_notice(doc->dev, "factory-marked bad block: %d\n",
 				   badblock);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index ff9db05..dbd9955 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -98,9 +98,13 @@ static int nand_get_device(struct mtd_info *mtd, int new_state);
 static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 			     struct mtd_oob_ops *ops);
 
+static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+			    struct mtd_oob_ops *ops);
+
 static int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
 			   int allowbbt);
 
+static int nand_bbt_erase_block(struct mtd_info *mtd, loff_t addr);
 /*
  * For devices which display every fart in the system on a separate LED. Is
  * compiled away when LED support is disabled.
@@ -531,13 +535,102 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
 		return nand_bbt_isreserved(bbt, ofs);
 }
 
+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
+ * @chip: 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 nand_create_badblock_pattern(struct nand_chip *chip)
+{
+	struct nand_bbt_descr *bd;
+
+	if (chip->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 = chip->bbt_options & BADBLOCK_SCAN_MASK;
+	bd->offs = chip->badblockpos;
+	bd->len = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+	bd->pattern = scan_ff_pattern;
+	bd->options |= NAND_BBT_DYNAMICSTRUCT;
+	chip->badblock_pattern = bd;
+
+	return 0;
+}
+
+static int nand_is_bad_bbm(struct mtd_info *mtd, loff_t addr)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct mtd_oob_ops ops;
+	struct nand_bbt_descr *bd = chip->badblock_pattern;
+	int j, ret;
+	int numpages;
+
+	if (bd->options & NAND_BBT_SCAN2NDPAGE)
+		numpages = 2;
+	else
+		numpages = 1;
+
+	ops.ooblen = mtd->oobsize;
+	ops.oobbuf = chip->buffers->databuf;
+	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 = nand_do_read_oob(mtd, addr, &ops);
+		/* Ignore ECC errors when checking for BBM */
+		if (ret && !mtd_is_bitflip_or_eccerr(ret))
+			return ret;
+
+		if (memcmp(chip->buffers->databuf + bd->offs,
+				bd->pattern, bd->len))
+			return 1;
+
+		addr += mtd->writesize;
+	}
+
+	return 0;
+}
+
 static int nand_default_bbt(struct mtd_info *mtd)
 {
 	struct nand_chip *chip = mtd->priv;
+	struct nand_bbt *bbt = kzalloc(sizeof(struct nand_bbt), GFP_KERNEL);
+
+	if (!bbt)
+		return -ENOMEM;
+
+	bbt->bbt_options = chip->bbt_options;
+	bbt->mtd = mtd;
+	bbt->numchips = chip->numchips;
+	bbt->chipsize = chip->chipsize;
+	bbt->chip_shift = chip->chip_shift;
+	bbt->bbt_erase_shift = chip->phys_erase_shift;
+	bbt->page_shift = chip->page_shift;
+	bbt->bbt_td = chip->bbt_td;
+	bbt->bbt_md = chip->bbt_md;
+	bbt->is_bad_bbm = nand_is_bad_bbm;
+	bbt->erase = nand_bbt_erase_block;
+	chip->nand_bbt = bbt;
 
 	return nand_bbt_init(chip->nand_bbt);
 }
 
+
 /**
  * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
  * @mtd: MTD device structure
@@ -4208,6 +4301,8 @@ int nand_scan_tail(struct mtd_info *mtd)
 	if (chip->options & NAND_SKIP_BBTSCAN)
 		return 0;
 
+	if (!chip->badblock_pattern && nand_create_badblock_pattern(chip))
+		return -ENOMEM;
 	/* Build bad block table */
 	return chip->scan_bbt(mtd);
 }
@@ -4271,6 +4366,11 @@ void nand_release(struct mtd_info *mtd)
 	kfree(chip->nand_bbt);
 	if (!(chip->options & NAND_OWN_BUFFERS))
 		kfree(chip->buffers);
+
+	/* Free bad block descriptor memory */
+	if (chip->badblock_pattern && chip->badblock_pattern->options
+			& NAND_BBT_DYNAMICSTRUCT)
+		kfree(chip->badblock_pattern);
 }
 EXPORT_SYMBOL_GPL(nand_release);
 
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 27269ea..ebbe6bf 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -245,7 +245,8 @@ static int read_bbt(struct nand_bbt *bbt, uint8_t *buf, int page, int num,
  * 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 nand_bbt *bbt, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+static int read_abs_bbt(struct nand_bbt *bbt, uint8_t *buf,
+			struct nand_bbt_descr *td, int chip)
 {
 	struct mtd_info *mtd = bbt->mtd;
 	int res = 0, i;
@@ -398,14 +399,13 @@ static void read_abs_bbts(struct nand_bbt *bbt, uint8_t *buf,
 /**
  * create_bbt - [GENERIC] Create a bad block table by scanning the device
  * @bbt: NAND BBT structure
- * @buf: temporary buffer
  * @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 nand_bbt *bbt, uint8_t *buf, int chip)
+static int create_bbt(struct nand_bbt *bbt, int chip)
 {
 	struct mtd_info *mtd = bbt->mtd;
 	int i, startblock, numblocks;
@@ -463,7 +463,8 @@ static int create_bbt(struct nand_bbt *bbt, uint8_t *buf, int chip)
  *
  * The bbt ident pattern resides in the oob area of the first page in a block.
  */
-static int search_bbt(struct nand_bbt *bbt, uint8_t *buf, struct nand_bbt_descr *td)
+static int search_bbt(struct nand_bbt *bbt, uint8_t *buf,
+			struct nand_bbt_descr *td)
 {
 	struct mtd_info *mtd = bbt->mtd;
 	int i, chips;
@@ -755,7 +756,7 @@ static int write_bbt(struct nand_bbt *bbt, uint8_t *buf,
  */
 static inline int nand_memory_bbt(struct nand_bbt *bbt)
 {
-	return create_bbt(bbt, NULL /* FIXME: this->buffers->databuf */, -1);
+	return create_bbt(bbt, -1);
 }
 
 /**
@@ -827,7 +828,7 @@ static int check_create(struct nand_bbt *bbt, uint8_t *buf)
 
 			/* Create the table in memory by scanning the chip(s) */
 			if (!(bbt->bbt_options & NAND_BBT_CREATE_EMPTY))
-				create_bbt(bbt, buf, chipsel);
+				create_bbt(bbt, chipsel);
 
 			td->version[i] = 1;
 			if (md)
@@ -912,7 +913,8 @@ static void mark_bbt_region(struct nand_bbt *bbt, struct nand_bbt_descr *td)
 		    !(td->options & NAND_BBT_WRITE)) {
 			if (td->pages[i] == -1)
 				continue;
-			block = td->pages[i] >> (bbt->bbt_erase_shift - bbt->page_shift);
+			block = td->pages[i] >>
+				(bbt->bbt_erase_shift - bbt->page_shift);
 			oldval = bbt_get_entry(bbt, block);
 			bbt_mark_entry(bbt, block, BBT_BLOCK_RESERVED);
 			if ((oldval != BBT_BLOCK_RESERVED) &&
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 539c9bf..fc53882 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -712,7 +712,6 @@ struct nand_chip {
 	struct nand_hw_control hwcontrol;
 
 	struct nand_bbt *nand_bbt;
-	uint8_t *bbt;
 	struct nand_bbt_descr *bbt_td;
 	struct nand_bbt_descr *bbt_md;
 
diff --git a/include/linux/mtd/nand_bbt.h b/include/linux/mtd/nand_bbt.h
index f776695..ed71e0e 100644
--- a/include/linux/mtd/nand_bbt.h
+++ b/include/linux/mtd/nand_bbt.h
@@ -83,7 +83,7 @@ struct nand_bbt_descr {
  * with NAND_BBT_CREATE.
  */
 #define NAND_BBT_CREATE_EMPTY	0x00000400
-/* Write bbt if neccecary */
+/* Write bbt if necessary */
 #define NAND_BBT_WRITE		0x00002000
 /* Read and write back block contents when writing bbt */
 #define NAND_BBT_SAVECONTENT	0x00004000
@@ -107,6 +107,12 @@ struct nand_bbt_descr {
  */
 #define NAND_BBT_NO_OOB_BBM	0x00080000
 
+/*
+ * Flag to mark that the nand_bbt_descr was allocated dynamicaly and must
+ * be freed in nand_release().
+ */
+#define NAND_BBT_DYNAMICSTRUCT	0x80000000
+
 /* The maximum number of blocks to scan for a bbt */
 #define NAND_BBT_SCAN_MAXBLOCKS	4
 
-- 
1.9.1


More information about the linux-mtd mailing list