[PATCH RESEND v2 3/3] cmd: mtd: add nand_read_test command support
Mikhail Kshevetskiy
mikhail.kshevetskiy at iopsys.eu
Wed Sep 24 06:20:33 CEST 2025
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 at 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
More information about the U-Boot
mailing list