forked from OERV-BSP/u-boot
cmd: mtd: add nand_read_test command support
This patch implements read-only test of nand flash devices. Test reads blocks of NAND flash in normal and raw modes and compares results. The following statuses can be returned for a block: * non-ecc reading failed, * ecc reading failed, * block is bad, * bitflips is above maximum, * actual number of biflips above reported one, * bitflips reached it maximum value, * bitflips above threshold, * block is ok. Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
This commit is contained in:
committed by
Michael Trimarchi
parent
b89b0f3c47
commit
642f150bcd
+16
@@ -1533,6 +1533,22 @@ config CMD_MTD_NAND_WRITE_TEST
|
||||
|
||||
WARNING: This test will destroy any data on blocks being tested.
|
||||
|
||||
config CMD_MTD_NAND_READ_TEST
|
||||
bool "mtd nand_read_test"
|
||||
depends on CMD_MTD
|
||||
help
|
||||
MTD nand_read_test command support.
|
||||
|
||||
Reads blocks of NAND flash in normal and raw modes and compares results.
|
||||
The following statuses can be returned for a block:
|
||||
* non-ecc reading failed,
|
||||
* ecc reading failed,
|
||||
* block is bad,
|
||||
* bitflips is above maximum,
|
||||
* actual number of biflips above reported one,
|
||||
* bitflips reached it maximum value,
|
||||
* block is ok.
|
||||
|
||||
config CMD_MUX
|
||||
bool "mux"
|
||||
depends on MULTIPLEXER
|
||||
|
||||
@@ -951,6 +951,200 @@ out_put_mtd:
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMD_MTD_NAND_READ_TEST
|
||||
enum nand_read_status {
|
||||
NAND_READ_STATUS_UNKNOWN = 0,
|
||||
NAND_READ_STATUS_NONECC_READ_FAIL,
|
||||
NAND_READ_STATUS_ECC_READ_FAIL,
|
||||
NAND_READ_STATUS_BAD_BLOCK,
|
||||
NAND_READ_STATUS_BITFLIP_ABOVE_MAX,
|
||||
NAND_READ_STATUS_BITFLIP_MISMATCH,
|
||||
NAND_READ_STATUS_BITFLIP_MAX,
|
||||
NAND_READ_STATUS_UNRELIABLE,
|
||||
NAND_READ_STATUS_OK,
|
||||
};
|
||||
|
||||
/* test_buf MUST be not smaller than 2 * blocksize bytes */
|
||||
static enum nand_read_status nand_read_block_check(struct mtd_info *mtd,
|
||||
loff_t off,
|
||||
size_t blocksize,
|
||||
u_char *test_buf)
|
||||
{
|
||||
struct mtd_oob_ops ops = {
|
||||
.mode = MTD_OPS_RAW,
|
||||
.len = blocksize,
|
||||
.datbuf = test_buf,
|
||||
};
|
||||
int i, d, ret, len, pos, cnt, max;
|
||||
|
||||
if (blocksize % mtd->writesize != 0) {
|
||||
printf("\r block at 0x%llx: bad block size\n", off);
|
||||
return NAND_READ_STATUS_UNKNOWN;
|
||||
}
|
||||
|
||||
ret = mtd->_read_oob(mtd, off, &ops);
|
||||
if (ret < 0) {
|
||||
printf("\r block at 0x%llx: non-ecc reading error %d\n",
|
||||
off, ret);
|
||||
return NAND_READ_STATUS_NONECC_READ_FAIL;
|
||||
}
|
||||
|
||||
ops.mode = MTD_OPS_PLACE_OOB;
|
||||
ops.datbuf = test_buf + blocksize;
|
||||
|
||||
ret = mtd->_read_oob(mtd, off, &ops);
|
||||
if (ret == -EBADMSG) {
|
||||
printf("\r block at 0x%llx: bad block\n", off);
|
||||
return NAND_READ_STATUS_BAD_BLOCK;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
printf("\r block at 0x%llx: ecc reading error %d\n", off, ret);
|
||||
return NAND_READ_STATUS_ECC_READ_FAIL;
|
||||
}
|
||||
|
||||
if (mtd->ecc_strength == 0)
|
||||
return NAND_READ_STATUS_OK;
|
||||
|
||||
if (ret > mtd->ecc_strength) {
|
||||
printf("\r block at 0x%llx: returned bit-flips value %d "
|
||||
"is above maximum value %d\n",
|
||||
off, ret, mtd->ecc_strength);
|
||||
return NAND_READ_STATUS_BITFLIP_ABOVE_MAX;
|
||||
}
|
||||
|
||||
max = 0;
|
||||
pos = 0;
|
||||
len = blocksize;
|
||||
while (len > 0) {
|
||||
cnt = 0;
|
||||
for (i = 0; i < mtd->ecc_step_size; i++) {
|
||||
d = test_buf[pos + i] ^ test_buf[blocksize + pos + i];
|
||||
if (d == 0)
|
||||
continue;
|
||||
|
||||
while (d > 0) {
|
||||
d &= (d - 1);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt > max)
|
||||
max = cnt;
|
||||
|
||||
len -= mtd->ecc_step_size;
|
||||
pos += mtd->ecc_step_size;
|
||||
}
|
||||
|
||||
if (max > ret) {
|
||||
printf("\r block at 0x%llx: bitflip mismatch, "
|
||||
"read %d but actual %d\n", off, ret, max);
|
||||
return NAND_READ_STATUS_BITFLIP_MISMATCH;
|
||||
}
|
||||
|
||||
if (ret == mtd->ecc_strength) {
|
||||
printf("\r block at 0x%llx: max bitflip reached, "
|
||||
"block is unreliable\n", off);
|
||||
return NAND_READ_STATUS_BITFLIP_MAX;
|
||||
}
|
||||
|
||||
if (ret >= mtd->bitflip_threshold) {
|
||||
printf("\r block at 0x%llx: bitflip threshold reached, "
|
||||
"block is unreliable\n", off);
|
||||
return NAND_READ_STATUS_UNRELIABLE;
|
||||
}
|
||||
|
||||
return NAND_READ_STATUS_OK;
|
||||
}
|
||||
|
||||
static int do_mtd_nand_read_test(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
u64 off, blocks;
|
||||
int stat[NAND_READ_STATUS_OK + 1];
|
||||
enum nand_read_status ret;
|
||||
u_char *buf;
|
||||
|
||||
if (argc < 2)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
mtd = get_mtd_by_name(argv[1]);
|
||||
if (IS_ERR_OR_NULL(mtd))
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
if (!mtd_can_have_bb(mtd)) {
|
||||
printf("Only NAND-based devices can be checked\n");
|
||||
goto test_error;
|
||||
}
|
||||
|
||||
if (!mtd->_read_oob) {
|
||||
printf("RAW reading is not supported\n");
|
||||
goto test_error;
|
||||
}
|
||||
|
||||
buf = malloc_cache_aligned(2 * mtd->erasesize);
|
||||
if (!buf) {
|
||||
printf("Can't allocate memory for the test\n");
|
||||
goto test_error;
|
||||
}
|
||||
|
||||
blocks = mtd->size;
|
||||
do_div(blocks, mtd->erasesize);
|
||||
|
||||
printf("ECC strength: %d\n", mtd->ecc_strength);
|
||||
printf("ECC theshold: %d\n", mtd->bitflip_threshold);
|
||||
printf("ECC step size: %d\n", mtd->ecc_step_size);
|
||||
printf("Erase block size: 0x%x\n", mtd->erasesize);
|
||||
printf("Total blocks: %lld\n", blocks);
|
||||
|
||||
printf("\nworking...\n");
|
||||
memset(stat, 0, sizeof(stat));
|
||||
for (off = 0; off < mtd->size; off += mtd->erasesize) {
|
||||
ret = nand_read_block_check(mtd, off, mtd->erasesize, buf);
|
||||
stat[ret]++;
|
||||
|
||||
switch (ret) {
|
||||
case NAND_READ_STATUS_BAD_BLOCK:
|
||||
case NAND_READ_STATUS_BITFLIP_MAX:
|
||||
case NAND_READ_STATUS_UNRELIABLE:
|
||||
if (!mtd_block_isbad(mtd, off))
|
||||
printf("\r block at 0x%llx: should be marked "
|
||||
"as BAD\n", off);
|
||||
break;
|
||||
|
||||
case NAND_READ_STATUS_OK:
|
||||
if (mtd_block_isbad(mtd, off))
|
||||
printf("\r block at 0x%llx: marked as BAD, but "
|
||||
"probably is GOOD\n", off);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
put_mtd_device(mtd);
|
||||
printf("\n");
|
||||
printf("results:\n");
|
||||
printf(" Good blocks: %d\n", stat[NAND_READ_STATUS_OK]);
|
||||
printf(" Physically bad blocks: %d\n", stat[NAND_READ_STATUS_BAD_BLOCK]);
|
||||
printf(" Unreliable blocks: %d\n", stat[NAND_READ_STATUS_BITFLIP_MAX] +
|
||||
stat[NAND_READ_STATUS_UNRELIABLE]);
|
||||
printf(" Non checked blocks: %d\n", stat[NAND_READ_STATUS_UNKNOWN]);
|
||||
printf(" Failed to check blocks: %d\n", stat[NAND_READ_STATUS_NONECC_READ_FAIL] +
|
||||
stat[NAND_READ_STATUS_ECC_READ_FAIL]);
|
||||
printf(" Suspictious blocks: %d\n", stat[NAND_READ_STATUS_BITFLIP_ABOVE_MAX] +
|
||||
stat[NAND_READ_STATUS_BITFLIP_MISMATCH]);
|
||||
return CMD_RET_SUCCESS;
|
||||
|
||||
test_error:
|
||||
put_mtd_device(mtd);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
@@ -1039,6 +1233,9 @@ U_BOOT_LONGHELP(mtd,
|
||||
#endif
|
||||
#if CONFIG_IS_ENABLED(CMD_MTD_NAND_WRITE_TEST)
|
||||
"mtd nand_write_test <name> [<off> [<size>]]\n"
|
||||
#endif
|
||||
#if CONFIG_IS_ENABLED(CMD_MTD_NAND_READ_TEST)
|
||||
"mtd nand_read_test <name>\n"
|
||||
#endif
|
||||
"\n"
|
||||
"With:\n"
|
||||
@@ -1081,6 +1278,11 @@ U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
|
||||
U_BOOT_SUBCMD_MKENT_COMPLETE(nand_write_test, 4, 0,
|
||||
do_nand_write_test,
|
||||
mtd_name_complete),
|
||||
#endif
|
||||
#if CONFIG_IS_ENABLED(CMD_MTD_NAND_READ_TEST)
|
||||
U_BOOT_SUBCMD_MKENT_COMPLETE(nand_read_test, 2, 0,
|
||||
do_mtd_nand_read_test,
|
||||
mtd_name_complete),
|
||||
#endif
|
||||
U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
|
||||
mtd_name_complete));
|
||||
|
||||
Reference in New Issue
Block a user