nand: Add verification functions
authorPeter Tyser <ptyser@xes-inc.com>
Tue, 3 Feb 2015 17:58:12 +0000 (11:58 -0600)
committerScott Wood <scottwood@freescale.com>
Tue, 31 Mar 2015 04:24:38 +0000 (23:24 -0500)
Add nand_verify() and nand_verify_page_oob().  nand_verify() verifies
NAND contents against an arbitrarily sized buffer using ECC while
nand_verify_page_oob() verifies a NAND page's contents and OOB.

Signed-off-by: Peter Tyser <ptyser@xes-inc.com>
Tested-by: Heiko Schocher <hs@denx.de>
Acked-by: Heiko Schocher <hs@denx.de>
drivers/mtd/nand/nand_util.c
include/nand.h

index afdd160d816a8935671ed5db51ce61424e60b8dd..f48775607101f50a239f6c00abbd11da8712526f 100644 (file)
@@ -463,6 +463,87 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
 }
 #endif
 
+/**
+ * nand_verify_page_oob:
+ *
+ * Verify a page of NAND flash, including the OOB.
+ * Reads page of NAND and verifies the contents and OOB against the
+ * values in ops.
+ *
+ * @param nand         NAND device
+ * @param ops          MTD operations, including data to verify
+ * @param ofs          offset in flash
+ * @return             0 in case of success
+ */
+int nand_verify_page_oob(nand_info_t *nand, struct mtd_oob_ops *ops, loff_t ofs)
+{
+       int rval;
+       struct mtd_oob_ops vops;
+       size_t verlen = nand->writesize + nand->oobsize;
+
+       memcpy(&vops, ops, sizeof(vops));
+
+       vops.datbuf = malloc(verlen);
+
+       if (!vops.datbuf)
+               return -ENOMEM;
+
+       vops.oobbuf = vops.datbuf + nand->writesize;
+
+       rval = mtd_read_oob(nand, ofs, &vops);
+       if (!rval)
+               rval = memcmp(ops->datbuf, vops.datbuf, vops.len);
+       if (!rval)
+               rval = memcmp(ops->oobbuf, vops.oobbuf, vops.ooblen);
+
+       free(vops.datbuf);
+
+       return rval ? -EIO : 0;
+}
+
+/**
+ * nand_verify:
+ *
+ * Verify a region of NAND flash.
+ * Reads NAND in page-sized chunks and verifies the contents against
+ * the contents of a buffer.  The offset into the NAND must be
+ * page-aligned, and the function doesn't handle skipping bad blocks.
+ *
+ * @param nand         NAND device
+ * @param ofs          offset in flash
+ * @param len          buffer length
+ * @param buf          buffer to read from
+ * @return             0 in case of success
+ */
+int nand_verify(nand_info_t *nand, loff_t ofs, size_t len, u_char *buf)
+{
+       int rval = 0;
+       size_t verofs;
+       size_t verlen = nand->writesize;
+       uint8_t *verbuf = malloc(verlen);
+
+       if (!verbuf)
+               return -ENOMEM;
+
+       /* Read the NAND back in page-size groups to limit malloc size */
+       for (verofs = ofs; verofs < ofs + len;
+            verofs += verlen, buf += verlen) {
+               verlen = min(nand->writesize, (uint32_t)(ofs + len - verofs));
+               rval = nand_read(nand, verofs, &verlen, verbuf);
+               if (!rval || (rval == -EUCLEAN))
+                       rval = memcmp(buf, verbuf, verlen);
+
+               if (rval)
+                       break;
+       }
+
+       free(verbuf);
+
+       return rval ? -EIO : 0;
+}
+
+
+
 /**
  * nand_write_skip_bad:
  *
@@ -501,7 +582,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
 
 #ifdef CONFIG_CMD_NAND_YAFFS
        if (flags & WITH_YAFFS_OOB) {
-               if (flags & ~WITH_YAFFS_OOB)
+               if (flags & (~WITH_YAFFS_OOB & ~WITH_WR_VERIFY))
                        return -EINVAL;
 
                int pages;
@@ -554,6 +635,10 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
 
        if (!need_skip && !(flags & WITH_DROP_FFS)) {
                rval = nand_write(nand, offset, length, buffer);
+
+               if ((flags & WITH_WR_VERIFY) && !rval)
+                       rval = nand_verify(nand, offset, *length, buffer);
+
                if (rval == 0)
                        return 0;
 
@@ -601,6 +686,11 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
                                ops.oobbuf = ops.datbuf + pagesize;
 
                                rval = mtd_write_oob(nand, offset, &ops);
+
+                               if ((flags & WITH_WR_VERIFY) && !rval)
+                                       rval = nand_verify_page_oob(nand,
+                                                       &ops, offset);
+
                                if (rval != 0)
                                        break;
 
@@ -620,6 +710,11 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
 
                        rval = nand_write(nand, offset, &truncated_write_size,
                                        p_buffer);
+
+                       if ((flags & WITH_WR_VERIFY) && !rval)
+                               rval = nand_verify(nand, offset,
+                                       truncated_write_size, p_buffer);
+
                        offset += write_size;
                        p_buffer += write_size;
                }
index 673597e80721ccc12767cea4c8c476b97925b071..8ea4d5d75caeb8313d40a26f24d49e043d63de50 100644 (file)
@@ -108,11 +108,15 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
                                  * is a 'mode' meaning it cannot be mixed with
                                  * other flags */
 #define WITH_DROP_FFS  (1 << 1) /* drop trailing all-0xff pages */
+#define WITH_WR_VERIFY (1 << 2) /* verify data was written correctly */
 
 int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
                        size_t *actual, loff_t lim, 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);
+int nand_verify_page_oob(nand_info_t *nand, struct mtd_oob_ops *ops,
+                       loff_t ofs);
+int nand_verify(nand_info_t *nand, loff_t ofs, size_t len, u_char *buf);
 
 #define NAND_LOCK_STATUS_TIGHT 0x01
 #define NAND_LOCK_STATUS_UNLOCK 0x04