Patchwork [U-Boot,v2] nand: Add torture feature

login
register
mail settings
Submitter Benoît Thébaudeau
Date Nov. 16, 2012, 7:20 p.m.
Message ID <1752254295.1495778.1353093654020.JavaMail.root@advansee.com>
Download mbox | patch
Permalink /patch/199718/
State Accepted
Headers show

Comments

Benoît Thébaudeau - Nov. 16, 2012, 7:20 p.m.
This patch adds a NAND Flash torture feature, which is useful as a block stress
test to determine if a block is still good and reliable (or should be marked as
bad), e.g. after a write error.

This code is ported from mtd-utils' lib/libmtd.c.

Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Cc: Scott Wood <scottwood@freescale.com>
---
Changes for v2:
 - Define and document CONFIG_CMD_NAND_TORTURE to selectively enable this
   feature.
 - Elaborate the documentation of the torture command.
 - Print an error message if the erase, read or write fails.
 - Rename the res variable to err in order to remove confusion with ret.

 .../common/cmd_nand.c                              |   22 ++++
 .../doc/README.nand                                |   21 ++++
 .../drivers/mtd/nand/nand_util.c                   |  123 ++++++++++++++++++++
 .../include/nand.h                                 |    3 +
 4 files changed, 169 insertions(+)

Patch

diff --git u-boot-nand-flash-9c60e75.orig/common/cmd_nand.c u-boot-nand-flash-9c60e75/common/cmd_nand.c
index 9c6dabe..639db81 100644
--- u-boot-nand-flash-9c60e75.orig/common/cmd_nand.c
+++ u-boot-nand-flash-9c60e75/common/cmd_nand.c
@@ -701,6 +701,25 @@  int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 		return ret == 0 ? 0 : 1;
 	}
 
+#ifdef CONFIG_CMD_NAND_TORTURE
+	if (strcmp(cmd, "torture") == 0) {
+		if (argc < 3)
+			goto usage;
+
+		if (!str2off(argv[2], &off)) {
+			puts("Offset is not a valid number\n");
+			return 1;
+		}
+
+		printf("\nNAND torture: device %d offset 0x%llx size 0x%x\n",
+			dev, off, nand->erasesize);
+		ret = nand_torture(nand, off);
+		printf(" %s\n", ret ? "Failed" : "Passed");
+
+		return ret == 0 ? 0 : 1;
+	}
+#endif
+
 	if (strcmp(cmd, "markbad") == 0) {
 		argc -= 2;
 		argv += 2;
@@ -812,6 +831,9 @@  U_BOOT_CMD(
 	"nand erase.chip [clean] - erase entire chip'\n"
 	"nand bad - show bad blocks\n"
 	"nand dump[.oob] off - dump page\n"
+#ifdef CONFIG_CMD_NAND_TORTURE
+	"nand torture off - torture block at offset\n"
+#endif
 	"nand scrub [-y] off size | scrub.part partition | scrub.chip\n"
 	"    really clean NAND erasing bad blocks (UNSAFE)\n"
 	"nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n"
diff --git u-boot-nand-flash-9c60e75.orig/doc/README.nand u-boot-nand-flash-9c60e75/doc/README.nand
index c130189..a1a511c 100644
--- u-boot-nand-flash-9c60e75.orig/doc/README.nand
+++ u-boot-nand-flash-9c60e75/doc/README.nand
@@ -108,6 +108,9 @@  Configuration Options:
    CONFIG_CMD_NAND
       Enables NAND support and commmands.
 
+   CONFIG_CMD_NAND_TORTURE
+      Enables the torture command (see description of this command below).
+
    CONFIG_MTD_NAND_ECC_JFFS2
       Define this if you want the Error Correction Code information in
       the out-of-band data to be formatted to match the JFFS2 file system.
@@ -213,6 +216,24 @@  Miscellaneous and testing commands:
   DANGEROUS!!! Factory set bad blocks will be lost. Use only
   to remove artificial bad blocks created with the "markbad" command.
 
+  "torture offset"
+  Torture block to determine if it is still reliable.
+  Enabled by the CONFIG_CMD_NAND_TORTURE configuration option.
+  This command returns 0 if the block is still reliable, else 1.
+  If the block is detected as unreliable, it is up to the user to decide to
+  mark this block as bad.
+  The analyzed block is put through 3 erase / write cycles (or less if the block
+  is detected as unreliable earlier).
+  This command can be used in scripts, e.g. together with the markbad command to
+  automate retries and handling of possibly newly detected bad blocks if the
+  nand write command fails.
+  It can also be used manually by users having seen some NAND errors in logs to
+  search the root cause of these errors.
+  The underlying nand_torture() function is also useful for code willing to
+  automate actions following a nand->write() error. This would e.g. be required
+  in order to program or update safely firmware to NAND, especially for the UBI
+  part of such firmware.
+
 
 NAND locking command (for chips with active LOCKPRE pin)
 
diff --git u-boot-nand-flash-9c60e75.orig/drivers/mtd/nand/nand_util.c u-boot-nand-flash-9c60e75/drivers/mtd/nand/nand_util.c
index 2855683..a162964 100644
--- u-boot-nand-flash-9c60e75.orig/drivers/mtd/nand/nand_util.c
+++ u-boot-nand-flash-9c60e75/drivers/mtd/nand/nand_util.c
@@ -683,3 +683,126 @@  int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
 
 	return 0;
 }
+
+#ifdef CONFIG_CMD_NAND_TORTURE
+
+/**
+ * check_pattern:
+ *
+ * Check if buffer contains only a certain byte pattern.
+ *
+ * @param buf buffer to check
+ * @param patt the pattern to check
+ * @param size buffer size in bytes
+ * @return 1 if there are only patt bytes in buf
+ *         0 if something else was found
+ */
+static int check_pattern(const u_char *buf, u_char patt, int size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		if (buf[i] != patt)
+			return 0;
+	return 1;
+}
+
+/**
+ * nand_torture:
+ *
+ * Torture a block of NAND flash.
+ * This is useful to determine if a block that caused a write error is still
+ * good or should be marked as bad.
+ *
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @return 0 if the block is still good
+ */
+int nand_torture(nand_info_t *nand, loff_t offset)
+{
+	u_char patterns[] = {0xa5, 0x5a, 0x00};
+	struct erase_info instr = {
+		.mtd = nand,
+		.addr = offset,
+		.len = nand->erasesize,
+	};
+	size_t retlen;
+	int err, ret = -1, i, patt_count;
+	u_char *buf;
+
+	if ((offset & (nand->erasesize - 1)) != 0) {
+		puts("Attempt to torture a block at a non block-aligned "
+				"offset\n");
+		return -EINVAL;
+	}
+
+	if (offset + nand->erasesize > nand->size) {
+		puts("Attempt to torture a block outside the flash area\n");
+		return -EINVAL;
+	}
+
+	patt_count = ARRAY_SIZE(patterns);
+
+	buf = malloc(nand->erasesize);
+	if (buf == NULL) {
+		puts("Out of memory for erase block buffer\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < patt_count; i++) {
+		err = nand->erase(nand, &instr);
+		if (err) {
+			printf("%s: erase() failed for block at 0x%llx: %d\n",
+				nand->name, instr.addr, err);
+			goto out;
+		}
+
+		/* Make sure the block contains only 0xff bytes */
+		err = nand->read(nand, offset, nand->erasesize, &retlen, buf);
+		if ((err && err != -EUCLEAN) || retlen != nand->erasesize) {
+			printf("%s: read() failed for block at 0x%llx: %d\n",
+				nand->name, instr.addr, err);
+			goto out;
+		}
+
+		err = check_pattern(buf, 0xff, nand->erasesize);
+		if (!err) {
+			printf("Erased block at 0x%llx, but a non-0xff byte "
+					"was found\n", offset);
+			ret = -EIO;
+			goto out;
+		}
+
+		/* Write a pattern and check it */
+		memset(buf, patterns[i], nand->erasesize);
+		err = nand->write(nand, offset, nand->erasesize, &retlen, buf);
+		if (err || retlen != nand->erasesize) {
+			printf("%s: write() failed for block at 0x%llx: %d\n",
+				nand->name, instr.addr, err);
+			goto out;
+		}
+
+		err = nand->read(nand, offset, nand->erasesize, &retlen, buf);
+		if ((err && err != -EUCLEAN) || retlen != nand->erasesize) {
+			printf("%s: read() failed for block at 0x%llx: %d\n",
+				nand->name, instr.addr, err);
+			goto out;
+		}
+
+		err = check_pattern(buf, patterns[i], nand->erasesize);
+		if (!err) {
+			printf("Pattern 0x%.2x checking failed for block at "
+					"0x%llx\n", patterns[i], offset);
+			ret = -EIO;
+			goto out;
+		}
+	}
+
+	ret = 0;
+
+out:
+	free(buf);
+	return ret;
+}
+
+#endif
diff --git u-boot-nand-flash-9c60e75.orig/include/nand.h u-boot-nand-flash-9c60e75/include/nand.h
index bbe28b2..c4530b0 100644
--- u-boot-nand-flash-9c60e75.orig/include/nand.h
+++ u-boot-nand-flash-9c60e75/include/nand.h
@@ -139,6 +139,9 @@  int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
 int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
 			u_char *buffer, int flags);
 int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts);
+#ifdef CONFIG_CMD_NAND_TORTURE
+int nand_torture(nand_info_t *nand, loff_t offset);
+#endif
 
 #define NAND_LOCK_STATUS_TIGHT	0x01
 #define NAND_LOCK_STATUS_UNLOCK 0x04