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.kshevets...@iopsys.eu> --- cmd/Kconfig | 16 +++++ cmd/mtd.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) diff --git a/cmd/Kconfig b/cmd/Kconfig index cd1a664f34f..21d6db57628 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -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 diff --git a/cmd/mtd.c b/cmd/mtd.c index 4d69c7b4915..689d8d11655 100644 --- a/cmd/mtd.c +++ b/cmd/mtd.c @@ -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)); -- 2.51.0