This patch implements readonly test of nand flashes.

Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevets...@iopsys.eu>
---
 cmd/Kconfig |   6 ++
 cmd/mtd.c   | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 209 insertions(+)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 698e0e697f2..db5113e016b 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1445,6 +1445,12 @@ config CMD_MTD_TORTURE
        help
          MTD torture command support.
 
+config CMD_MTD_NANDTEST
+       bool "mtd nandtest"
+       depends on CMD_MTD
+       help
+         MTD nandtest command support.
+
 config CMD_MUX
        bool "mux"
        depends on MULTIPLEXER
diff --git a/cmd/mtd.c b/cmd/mtd.c
index 69ddfe1d9c6..4a380dcaf83 100644
--- a/cmd/mtd.c
+++ b/cmd/mtd.c
@@ -931,6 +931,202 @@ out_put_mtd:
 }
 #endif
 
+#ifdef CONFIG_CMD_MTD_NANDTEST
+enum nandtest_status {
+       NANDTEST_STATUS_UNKNOWN = 0,
+       NANDTEST_STATUS_NONECC_READ_FAIL,
+       NANDTEST_STATUS_ECC_READ_FAIL,
+       NANDTEST_STATUS_BAD_BLOCK,
+       NANDTEST_STATUS_BITFLIP_ABOVE_MAX,
+       NANDTEST_STATUS_BITFLIP_MISMATCH,
+       NANDTEST_STATUS_BITFLIP_MAX,
+       NANDTEST_STATUS_OK,
+};
+
+static enum nandtest_status nandtest_block_check(struct mtd_info *mtd,
+                                                loff_t off, size_t blocksize)
+{
+       struct mtd_oob_ops      ops = {};
+       u_char                  *buf;
+       int                     i, d, ret, len, pos, cnt, max;
+
+       if (blocksize % mtd->writesize != 0) {
+               printf("\r  block at %llx: bad block size\n", off);
+               return NANDTEST_STATUS_UNKNOWN;
+       }
+
+       buf = malloc_cache_aligned(2 * blocksize);
+       if (buf == NULL) {
+               printf("\r  block at %llx: can't allocate memory\n", off);
+               return NANDTEST_STATUS_UNKNOWN;
+       }
+
+       ops.mode = MTD_OPS_RAW;
+       ops.len = blocksize;
+       ops.datbuf = buf;
+       ops.ooblen = 0;
+       ops.oobbuf = NULL;
+
+       if (mtd->_read_oob)
+               ret = mtd->_read_oob(mtd, off, &ops);
+       else
+               ret = mtd->_read(mtd, off, ops.len, &ops.retlen, ops.datbuf);
+
+       if (ret != 0) {
+               free(buf);
+               printf("\r  block at %llx: non-ecc reading error %d\n",
+                      off, ret);
+               return NANDTEST_STATUS_NONECC_READ_FAIL;
+       }
+
+       ops.mode = MTD_OPS_AUTO_OOB;
+       ops.datbuf = buf + blocksize;
+
+       if (mtd->_read_oob)
+               ret = mtd->_read_oob(mtd, off, &ops);
+       else
+               ret = mtd->_read(mtd, off, ops.len, &ops.retlen, ops.datbuf);
+
+       if (ret == -EBADMSG) {
+               free(buf);
+               printf("\r  block at %llx: bad block\n", off);
+               return NANDTEST_STATUS_BAD_BLOCK;
+       }
+
+       if (ret < 0) {
+               free(buf);
+               printf("\r  block at %llx: ecc reading error %d\n", off, ret);
+               return NANDTEST_STATUS_ECC_READ_FAIL;
+       }
+
+       if (mtd->ecc_strength == 0) {
+               free(buf);
+               return NANDTEST_STATUS_OK;
+       }
+
+       if (ret > mtd->ecc_strength) {
+               free(buf);
+               printf("\r  block at %llx: returned bit-flips value %d "
+                      "is above maximum value %d\n",
+                      off, ret, mtd->ecc_strength);
+               return NANDTEST_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 = buf[pos + i] ^ 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;
+       }
+
+       free(buf);
+
+       if (max > ret) {
+               printf("\r  block at %llx: bitflip mismatch, "
+                      "read %d but actual %d\n", off, ret, max);
+               return NANDTEST_STATUS_BITFLIP_MISMATCH;
+       }
+
+       if (ret == mtd->ecc_strength) {
+               printf("\r  block at %llx: max bitflip reached, "
+                      "block is unreliable\n", off);
+               return NANDTEST_STATUS_BITFLIP_MAX;
+       }
+
+       return NANDTEST_STATUS_OK;
+}
+
+static int do_mtd_nandtest(struct cmd_tbl *cmdtp, int flag, int argc,
+                          char *const argv[])
+{
+       struct mtd_info         *mtd;
+       loff_t                  off, len;
+       int                     stat[NANDTEST_STATUS_OK + 1];
+       enum nandtest_status    ret;
+       size_t                  block_size;
+
+       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 out_put_mtd;
+       }
+
+       off = 0;
+       len = mtd->size;
+       block_size = mtd->erasesize;
+
+       printf("ECC strength:     %d\n",   mtd->ecc_strength);
+       printf("ECC step size:    %d\n",   mtd->ecc_step_size);
+       printf("Erase block size: 0x%x\n", mtd->erasesize);
+       printf("Total blocks:     %d\n",   (u32)len / mtd->erasesize);
+
+       memset(stat, 0, sizeof(stat));
+       while (len > 0) {
+               if (mtd_is_aligned_with_block_size(mtd, off))
+                       printf("\r  block at %llx ", off);
+
+               ret = nandtest_block_check(mtd, off, block_size);
+               stat[ret]++;
+
+               switch (ret) {
+               case NANDTEST_STATUS_BAD_BLOCK:
+               case NANDTEST_STATUS_BITFLIP_MAX:
+                       if (!mtd_block_isbad(mtd, off))
+                               printf("\r  block at %llx: should be marked "
+                                      "as BAD\n", off);
+                       break;
+
+               case NANDTEST_STATUS_OK:
+                       if (mtd_block_isbad(mtd, off))
+                               printf("\r  block at %llx: marked as BAD, but "
+                                      "probably is GOOD\n", off);
+                       break;
+
+               default:
+                       break;
+               }
+
+               off += block_size;
+               len -= block_size;
+       }
+
+out_put_mtd:
+       put_mtd_device(mtd);
+       printf("\n");
+       printf("results:\n");
+       printf("  Good blocks:            %d\n", stat[NANDTEST_STATUS_OK]);
+       printf("  Physically bad blocks:  %d\n", 
stat[NANDTEST_STATUS_BAD_BLOCK]);
+       printf("  Unreliable blocks:      %d\n", 
stat[NANDTEST_STATUS_BITFLIP_MAX]);
+       printf("  Non checked blocks:     %d\n", stat[NANDTEST_STATUS_UNKNOWN]);
+       printf("  Failed to check blocks: %d\n", 
stat[NANDTEST_STATUS_NONECC_READ_FAIL] +
+                                                
stat[NANDTEST_STATUS_ECC_READ_FAIL]);
+       printf("  Suspictious blocks:     %d\n", 
stat[NANDTEST_STATUS_BITFLIP_ABOVE_MAX] +
+                                                
stat[NANDTEST_STATUS_BITFLIP_MISMATCH]);
+       return CMD_RET_SUCCESS;
+}
+#endif
+
 static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc,
                      char *const argv[])
 {
@@ -1019,6 +1215,9 @@ U_BOOT_LONGHELP(mtd,
 #endif
 #ifdef CONFIG_CMD_MTD_TORTURE
        "mtd torture                           <name>        [<off> [<size>]]\n"
+#endif
+#ifdef CONFIG_CMD_MTD_NANDTEST
+       "mtd nandtest                          <name>\n"
 #endif
        "\n"
        "With:\n"
@@ -1060,6 +1259,10 @@ U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
 #ifdef CONFIG_CMD_MTD_TORTURE
                U_BOOT_SUBCMD_MKENT_COMPLETE(torture, 4, 0, do_mtd_torture,
                                             mtd_name_complete),
+#endif
+#ifdef CONFIG_CMD_MTD_NANDTEST
+               U_BOOT_SUBCMD_MKENT_COMPLETE(nandtest, 2, 0, do_mtd_nandtest,
+                                            mtd_name_complete),
 #endif
                U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
                                             mtd_name_complete));
-- 
2.45.2

Reply via email to