nand: Add torture feature
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> [scottwood@freescale.com: removed unnec. ifdef and unwrapped error strings] Signed-off-by: Scott Wood <scottwood@freescale.com>
This commit is contained in:
parent
8156f732ee
commit
3287f6d385
@ -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"
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user