diff options
| -rw-r--r-- | common/cmd_nand.c | 22 | ||||
| -rw-r--r-- | doc/README.nand | 21 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_util.c | 122 | ||||
| -rw-r--r-- | include/nand.h | 1 | 
4 files changed, 166 insertions, 0 deletions
| diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 4b1606972..1568594ca 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -700,6 +700,25 @@ static 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; @@ -810,6 +829,9 @@ static char nand_help_text[] =  	"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 a/doc/README.nand b/doc/README.nand index c13018958..a1a511c53 100644 --- a/doc/README.nand +++ b/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 a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 285568316..2ba0c5ef9 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -683,3 +683,125 @@ 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 a/include/nand.h b/include/nand.h index bbe28b20b..dded4e27f 100644 --- a/include/nand.h +++ b/include/nand.h @@ -139,6 +139,7 @@ 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); +int nand_torture(nand_info_t *nand, loff_t offset);  #define NAND_LOCK_STATUS_TIGHT	0x01  #define NAND_LOCK_STATUS_UNLOCK 0x04 |