[U-Boot] [PATCH 1/1] Fix issues and add new commands for onenand.

Manikandan Pillai mani.pillai at ti.com
Wed Jan 7 04:26:25 CET 2009


This patch has been generated against the tip of u-boot-arm git - origin/omap3 
branch.
This patch provides for a few more commands for onenand in tune with what
is avaiable for NAND chips and also debugging functions like scrub and 
markbad. The bad block checking has been fixed for errors since the new
framework was failing in reading OOB data.


Signed-off-by: Manikandan Pillai <mani.pillai at ti.com>
---
 common/cmd_onenand.c               |  101 +++++++++++++++++-----
 drivers/mtd/onenand/onenand_base.c |  168 +++++++++++++++++++++++++++++++++---
 drivers/mtd/onenand/onenand_bbt.c  |   24 +++---
 include/onenand_uboot.h            |    7 ++
 4 files changed, 253 insertions(+), 47 deletions(-)

diff --git a/common/cmd_onenand.c b/common/cmd_onenand.c
index 8d87b78..1a5c1c2 100644
--- a/common/cmd_onenand.c
+++ b/common/cmd_onenand.c
@@ -24,6 +24,7 @@ extern struct onenand_chip onenand_chip;
 int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 {
 	int ret = 0;
+	loff_t	ofs = 0;
 
 	switch (argc) {
 	case 0:
@@ -36,23 +37,53 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 			onenand_init();
 			return 0;
 		}
+		if (strncmp(argv[1], "info", 4) != 0)
+			printf("OneNAND: Incorrect command/number of args\n");
 		printf("%s\n", onenand_mtd.name);
 		return 0;
 
+	case 3:
+		if ((strncmp(argv[1], "markbad", 7) == 0) && (argc == 3)) {
+			int ret ;
+			ofs = simple_strtoul(argv[2], NULL, 16);
+			if (ofs >= onenand_mtd.size) {
+				printf("Error : offset exceeds size\n");
+				return 0;
+			} else
+				ret = onenand_block_markbad(&onenand_mtd, ofs);
+
+			if (ret)
+				printf("Error marking bad-block\n");
+			else
+				printf("Done\n");
+			return 0;
+		}
+		printf("OneNAND : wrong number of arguments\n");
+		onenand_print_device_info(onenand_chip.device_id);
+		printf("Usage:\n%s\n", cmdtp->usage);
+		return 0;
+
 	default:
 		/* At least 4 args */
-		if (strncmp(argv[1], "erase", 5) == 0) {
+		if (((strncmp(argv[1], "erase", 5) == 0) ||
+			(strncmp(argv[1], "scrub", 5) == 0)) &&
+			(argc == 4)) {
 			struct erase_info instr = {
 				.callback	= NULL,
 			};
-			ulong start, end;
-			ulong block;
+			ulong start = 0, end = 0;
+			ulong block = 0;
 			char *endtail;
 
 			if (strncmp(argv[2], "block", 5) == 0) {
 				start = simple_strtoul(argv[3], NULL, 10);
 				endtail = strchr(argv[3], '-');
 				end = simple_strtoul(endtail + 1, NULL, 10);
+				if (end < start) {
+					printf("Error : erase failed - ");
+					printf("end block incorrect\n");
+					break;
+				}
 			} else {
 				start = simple_strtoul(argv[2], NULL, 10);
 				end = simple_strtoul(argv[3], NULL, 10);
@@ -63,14 +94,21 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 				end--;
 			}
 
-			if (!end || end < 0)
-				end = start;
+			if (!end || (end < start)) {
+				printf("Error : erase failed ");
+				printf("end address incorrect\n");
+				break;
+			}
 
 			printf("Erase block from %lu to %lu\n", start, end);
 
 			for (block = start; block <= end; block++) {
 				instr.addr = block << onenand_chip.erase_shift;
 				instr.len = 1 << onenand_chip.erase_shift;
+				if (strncmp(argv[1], "scrub", 5) == 0)
+					instr.priv = ONENAND_SCRUB;
+				else
+					instr.priv = 0;
 				ret = onenand_erase(&onenand_mtd, &instr);
 				if (ret) {
 					printf("erase failed %lu\n", block);
@@ -79,9 +117,8 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 			}
 
 			return 0;
-		}
-
-		if (strncmp(argv[1], "read", 4) == 0) {
+		} else if ((strncmp(argv[1], "read", 4) == 0) &&
+				(argc == 5)) {
 			ulong addr = simple_strtoul(argv[2], NULL, 16);
 			ulong ofs = simple_strtoul(argv[3], NULL, 16);
 			size_t len = simple_strtoul(argv[4], NULL, 16);
@@ -95,6 +132,7 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 				ops.datbuf = NULL;
 				ops.ooblen = len;
 				ops.oobbuf = (u_char *) addr;
+				ops.ooboffs = 0;
 			} else {
 				ops.len = len;
 				ops.datbuf = (u_char *) addr;
@@ -103,37 +141,44 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 			}
 			ops.retlen = ops.oobretlen = 0;
 
-			onenand_mtd.read_oob(&onenand_mtd, ofs, &ops);
-			printf("Done\n");
+			ret = onenand_mtd.read_oob(&onenand_mtd, ofs, &ops);
+			if (ret)
+				printf("Error reading from device\n");
+			else
+				printf("Done\n");
 
 			return 0;
-		}
 
-		if (strncmp(argv[1], "write", 5) == 0) {
+		} else if ((strncmp(argv[1], "write", 5) == 0) &&
+			(argc == 5)) {
 			ulong addr = simple_strtoul(argv[2], NULL, 16);
 			ulong ofs = simple_strtoul(argv[3], NULL, 16);
 			size_t len = simple_strtoul(argv[4], NULL, 16);
 			size_t retlen = 0;
 
-			onenand_write(&onenand_mtd, ofs, len, &retlen,
+			ret = onenand_write(&onenand_mtd, ofs, len, &retlen,
 				      (u_char *) addr);
-			printf("Done\n");
+			if (ret)
+				printf("Error writing to device\n");
+			else
+				printf("Done\n");
 
 			return 0;
-		}
-
-		if (strncmp(argv[1], "block", 5) == 0) {
+		} else if (strncmp(argv[1], "block", 5) == 0) {
 			ulong addr = simple_strtoul(argv[2], NULL, 16);
 			ulong block = simple_strtoul(argv[3], NULL, 10);
-			ulong page = simple_strtoul(argv[4], NULL, 10);
-			size_t len = simple_strtol(argv[5], NULL, 10);
+			ulong page;
+			size_t len;
 			ulong ofs;
 			int oob = strncmp(argv[1], "block.oob", 9) ? 0 : 1;
 			struct mtd_oob_ops ops;
 
 			ops.mode = MTD_OOB_PLACE;
 
-
+			if (argc >= 5)
+				page = simple_strtoul(argv[4], NULL, 10);
+			if (argc >= 6)
+				len = simple_strtol(argv[5], NULL, 10);
 			ofs = block << onenand_chip.erase_shift;
 			if (page)
 				ofs += page << onenand_chip.page_shift;
@@ -156,6 +201,10 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 
 			onenand_read_oob(&onenand_mtd, ofs, &ops);
 			return 0;
+		} else {
+			printf("OneNAND : wrong number of arguments\n");
+			onenand_print_device_info(onenand_chip.device_id);
+			printf("Usage:\n%s\n", cmdtp->usage);
 		}
 
 		break;
@@ -169,8 +218,16 @@ U_BOOT_CMD(
 	"onenand - OneNAND sub-system\n",
 	"info   - show available OneNAND devices\n"
 	"onenand read[.oob] addr ofs len - read data at ofs with len to addr\n"
+	"       ---ofs and len(not for oob) have to be page aligned\n"
 	"onenand write addr ofs len - write data at ofs with len from addr\n"
-	"onenand erase saddr eaddr - erase block start addr to end addr\n"
+	"       ---ofs and len have to be page aligned\n"
+	"onenand erase sofs eofs - erase block start addr ofs to end addr ofs\n"
+	"onenand erase block sblk-endblk - erase blocks sblk to endblk\n"
+	"       ---erase command does not erase bad blocks\n"
+	"onenand scrub block start-end - erase block from start to end\n"
+	"onenand scrub sofs eofs - erase blocks start ofs to end ofs\n"
+	"       ---CAUTION :scrub command erases bad blocks also\n"
 	"onenand block[.oob] addr block [page] [len] - "
-		"read data with (block [, page]) to addr"
+		"read data with (block [, page]) to addr\n"
+	"onenand markbad ofs - mark bad-block at ofs\n"
 );
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 9b7bf3a..a367cc3 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -23,6 +23,36 @@
 #include <asm/errno.h>
 #include <malloc.h>
 
+/**
+ * onenand_oob_64 - oob info for large (2KB) page
+ */
+static struct nand_ecclayout onenand_oob_64 = {
+	.eccbytes	= 20,
+	.eccpos		= {
+		8, 9, 10, 11, 12,
+		24, 25, 26, 27, 28,
+		40, 41, 42, 43, 44,
+		56, 57, 58, 59, 60,
+		},
+	.oobfree	= {
+		{2, 3}, {14, 2}, {18, 3}, {30, 2},
+		{34, 3}, {46, 2}, {50, 3}, {62, 2}
+	}
+};
+
+/**
+ * onenand_oob_32 - oob info for middle (1KB) page
+ */
+static struct nand_ecclayout onenand_oob_32 = {
+	.eccbytes	= 10,
+	.eccpos		= {
+		8, 9, 10, 11, 12,
+		24, 25, 26, 27, 28,
+		},
+	.oobfree	= { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
+};
+
+
 /* It should access 16-bit instead of 8-bit */
 static inline void *memcpy_16(void *dst, const void *src, unsigned int len)
 {
@@ -272,6 +302,58 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
 }
 
 /**
++ * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
++ * @param mtd           MTD device structure
++ * @param ofs           offset from device start
++ * @param getchip       0, if the chip is already selected
++ * @param 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.
++ */
+int onenand_block_checkbad(struct mtd_info *mtd, int ofs, int getchip,
+			int allowbbt)
+{
+	struct onenand_chip *this = mtd->priv;
+	struct bbm_info *bbm = this->bbm;
+
+	/* Return info from the table */
+	return bbm->isbad_bbt(mtd, ofs, allowbbt);
+}
+
+/**
++ * onenand_default_block_markbad - [DEFAULT] mark a block bad
++ * @param mtd           MTD device structure
++ * @param ofs           offset from device start
++ *
++ * This is the default implementation, which can be overridden by
++ * a hardware specific driver.
++ */
+int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct onenand_chip *this = mtd->priv;
+	struct bbm_info *bbm = this->bbm;
+	u_char buf[2] = {0x0A, 0x0A};
+	int block;
+	struct mtd_oob_ops oob_ops;
+
+	/* Get block number */
+	block = ((int) ofs) >> bbm->bbt_erase_shift;
+	if (bbm->bbt)
+		bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+	/* We write two bytes, so we dont have to mess with 16 bit access */
+	ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
+	oob_ops.mode = MTD_OOB_AUTO;
+	oob_ops.len = 2;
+	oob_ops.ooblen = 2;
+	oob_ops.datbuf = NULL;
+	oob_ops.oobbuf = buf;
+	oob_ops.ooboffs = 0;
+	return onenand_write_oob(mtd, ofs, &oob_ops);
+}
+
+/**
  * onenand_wait - [DEFAULT] wait until the command is done
  * @param mtd		MTD device structure
  * @param state		state to select the max. timeout value
@@ -733,6 +815,8 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
 	/* Initialize return length value */
 	ops->oobretlen = 0;
 
+	stats = mtd->ecc_stats;
+
 	if (mode == MTD_OOB_AUTO)
 		oobsize = this->ecclayout->oobavail;
 	else
@@ -744,17 +828,11 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n");
 		return -EINVAL;
 	}
-
-	/* Do not allow reads past end of device */
-	if (unlikely(from >= mtd->size ||
-		column + len > ((mtd->size >> this->page_shift) -
-				(from >> this->page_shift)) * oobsize)) {
+	if (from >= mtd->size) {
 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n");
 		return -EINVAL;
 	}
 
-	stats = mtd->ecc_stats;
-
 	while (read < len) {
 		thislen = oobsize - column;
 		thislen = min_t(int, thislen, len);
@@ -790,13 +868,11 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
 	}
 
 	ops->oobretlen = read;
-
 	if (ret)
 		return ret;
 
 	if (mtd->ecc_stats.failed - stats.failed)
 		return -EBADMSG;
-
 	return 0;
 }
 
@@ -1250,7 +1326,6 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
 
 	/* Initialize retlen, in case of early exit */
 	ops->oobretlen = 0;
-
 	if (mode == MTD_OOB_AUTO)
 		oobsize = this->ecclayout->oobavail;
 	else
@@ -1293,6 +1368,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
 			onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
 		else
 			memcpy(oobbuf + column, buf, thislen);
+
 		this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
 
 		this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1347,6 +1423,7 @@ int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
 		.ooblen = 0,
 		.datbuf = (u_char *) buf,
 		.oobbuf = NULL,
+		.ooboffs = 0,
 	};
 	int ret;
 
@@ -1387,7 +1464,6 @@ int onenand_write_oob(struct mtd_info *mtd, loff_t to,
 	else
 		ret = onenand_write_oob_nolock(mtd, to, ops);
 	onenand_release_device(mtd);
-
 	return ret;
 
 }
@@ -1472,6 +1548,34 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 
 		onenand_invalidate_bufferram(mtd, addr, block_size);
 
+		if (instr->priv == ONENAND_SCRUB) {
+			if (onenand_block_isbad(mtd, addr) != 0x3) {
+				/* erase user marked bad blocks only */
+				this->command(mtd, ONENAND_CMD_ERASE,
+						addr, block_size);
+			} else {
+				/* skip the factory marked bad blocks */
+				printf("onenand_erase: not erasing\
+				factory bad blk @0x%x\n", (int)addr);
+				len -= block_size;
+				addr += block_size;
+				continue;
+			}
+		} else {
+			if (onenand_block_isbad(mtd, addr) == 0) {
+				/* block is not a known bad block. Erase it */
+				this->command(mtd, ONENAND_CMD_ERASE,\
+						addr, block_size);
+			} else {
+				/* skip the user and factory bad blocks */
+				printf("onenand_erase: not erasing\
+					bad block @0x%x\n", (int)addr);
+				len -= block_size;
+				addr += block_size;
+				continue;
+			}
+		}
+
 		ret = this->wait(mtd, FL_ERASING);
 		/* Check, if it is write protected */
 		if (ret) {
@@ -1563,7 +1667,6 @@ int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 			return 0;
 		return ret;
 	}
-
 	ret = this->block_markbad(mtd, ofs);
 	return ret;
 }
@@ -1761,7 +1864,6 @@ static int onenand_probe(struct mtd_info *mtd)
 	this->writesize = mtd->writesize;
 
 	/* REVIST: Multichip handling */
-
 	mtd->size = this->chipsize;
 
 	/* Version ID */
@@ -1802,6 +1904,7 @@ static int onenand_probe(struct mtd_info *mtd)
  */
 int onenand_scan(struct mtd_info *mtd, int maxchips)
 {
+	unsigned int i;
 	struct onenand_chip *this = mtd->priv;
 
 	if (!this->read_word)
@@ -1819,6 +1922,45 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 	if (!this->write_bufferram)
 		this->write_bufferram = onenand_write_bufferram;
 
+	if (!this->block_markbad)
+		this->block_markbad = onenand_default_block_markbad;
+
+	/*
+	 * Allow subpage writes up to oobsize.
+	 */
+	switch (mtd->oobsize) {
+	case 64:
+		this->ecclayout = &onenand_oob_64;
+		mtd->subpage_sft = 2;
+		break;
+
+	case 32:
+		this->ecclayout = &onenand_oob_32;
+		mtd->subpage_sft = 1;
+		break;
+
+	default:
+		printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
+			mtd->oobsize);
+		mtd->subpage_sft = 0;
+		/* To prevent kernel oops */
+		this->ecclayout = &onenand_oob_32;
+		break;
+	}
+
+	/*
+	 * The number of bytes available for a client to place data into
+	 * the out of band area
+	 */
+	this->ecclayout->oobavail = 0;
+	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
+	    this->ecclayout->oobfree[i].length; i++)
+		this->ecclayout->oobavail +=
+			this->ecclayout->oobfree[i].length;
+	mtd->oobavail = this->ecclayout->oobavail;
+
+	mtd->ecclayout = this->ecclayout;
+
 	if (onenand_probe(mtd))
 		return -ENXIO;
 
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index f6092b9..e750da5 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -69,14 +69,16 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf,
 	loff_t from;
 	size_t readlen, ooblen;
 	struct mtd_oob_ops ops;
+	uint8_t oobbuf[16];
 
 	printk(KERN_INFO "Scanning device for bad blocks\n");
 
-	len = 1;
+	len = 2;
 
 	/* We need only read few bytes from the OOB area */
 	scanlen = ooblen = 0;
 	readlen = bd->len;
+	scanlen = bd->len;
 
 	/* chip == -1 case only */
 	/* Note that numblocks is 2 * (real numblocks) here;
@@ -88,29 +90,27 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf,
 
 	ops.mode = MTD_OOB_PLACE;
 	ops.ooblen = readlen;
-	ops.oobbuf = buf;
-	ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
+	ops.oobbuf = &oobbuf[0] ;
+	ops.datbuf = NULL;
+	ops.len = ops.retlen = ops.oobretlen = 0;
+	ops.ooboffs = 2;
 
 	for (i = startblock; i < numblocks;) {
 		int ret;
-
 		for (j = 0; j < len; j++) {
 			/* No need to read pages fully,
-			 * just read required OOB bytes */
-			ret = onenand_bbt_read_oob(mtd,
-					     from + j * mtd->writesize +
-					     bd->offs, &ops);
-
+			* just read required OOB bytes */
+			ret = onenand_read_oob(mtd, from, &ops);
 			/* If it is a initial bad block, just ignore it */
 			if (ret == ONENAND_BBT_READ_FATAL_ERROR)
 				return -EIO;
 
 			if (ret || check_short_pattern
-			    (&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
+				(ops.oobbuf, scanlen, mtd->writesize, bd)) {
 				bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
 				printk(KERN_WARNING
-				       "Bad eraseblock %d at 0x%08x\n", i >> 1,
-				       (unsigned int)from);
+					"Bad eraseblock %d at 0x%08x\n", i >> 1,
+					(unsigned int)from);
 				break;
 			}
 		}
diff --git a/include/onenand_uboot.h b/include/onenand_uboot.h
index e960257..12cfd77 100644
--- a/include/onenand_uboot.h
+++ b/include/onenand_uboot.h
@@ -20,6 +20,8 @@
 struct mtd_info;
 struct erase_info;
 
+#define	ONENAND_SCRUB	0x10
+
 extern struct mtd_info onenand_mtd;
 
 /* Functions */
@@ -30,10 +32,15 @@ extern int onenand_read_oob(struct mtd_info *mtd, loff_t from,
 			    struct mtd_oob_ops *ops);
 extern int onenand_write(struct mtd_info *mtd, loff_t from, size_t len,
 			 size_t * retlen, const u_char * buf);
+extern int onenand_write_oob(struct mtd_info *mtd, loff_t to,
+			struct mtd_oob_ops *oob_ops);
 extern int onenand_erase(struct mtd_info *mtd, struct erase_info *instr);
 
 extern int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
 
 extern char *onenand_print_device_info(int device);
 
+extern int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs);
+extern int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs);
+
 #endif /* __UBOOT_ONENAND_H */
-- 
1.5.6



More information about the U-Boot mailing list