|
@@ -683,3 +683,125 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_CMD_NAND_TORTURE
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * check_pattern:
|
|
|
|
+ *
|
|
|
|
+ * Check if buffer contains only a certain byte pattern.
|
|
|
|
+ *
|
|
|
|
+ * @param buf buffer to check
|
|
|
|
+ * @param patt the pattern to check
|
|
|
|
+ * @param size buffer size in bytes
|
|
|
|
+ * @return 1 if there are only patt bytes in buf
|
|
|
|
+ * 0 if something else was found
|
|
|
|
+ */
|
|
|
|
+static int check_pattern(const u_char *buf, u_char patt, int size)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < size; i++)
|
|
|
|
+ if (buf[i] != patt)
|
|
|
|
+ return 0;
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * nand_torture:
|
|
|
|
+ *
|
|
|
|
+ * Torture a block of NAND flash.
|
|
|
|
+ * This is useful to determine if a block that caused a write error is still
|
|
|
|
+ * good or should be marked as bad.
|
|
|
|
+ *
|
|
|
|
+ * @param nand NAND device
|
|
|
|
+ * @param offset offset in flash
|
|
|
|
+ * @return 0 if the block is still good
|
|
|
|
+ */
|
|
|
|
+int nand_torture(nand_info_t *nand, loff_t offset)
|
|
|
|
+{
|
|
|
|
+ u_char patterns[] = {0xa5, 0x5a, 0x00};
|
|
|
|
+ struct erase_info instr = {
|
|
|
|
+ .mtd = nand,
|
|
|
|
+ .addr = offset,
|
|
|
|
+ .len = nand->erasesize,
|
|
|
|
+ };
|
|
|
|
+ size_t retlen;
|
|
|
|
+ int err, ret = -1, i, patt_count;
|
|
|
|
+ u_char *buf;
|
|
|
|
+
|
|
|
|
+ if ((offset & (nand->erasesize - 1)) != 0) {
|
|
|
|
+ puts("Attempt to torture a block at a non block-aligned offset\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (offset + nand->erasesize > nand->size) {
|
|
|
|
+ puts("Attempt to torture a block outside the flash area\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ patt_count = ARRAY_SIZE(patterns);
|
|
|
|
+
|
|
|
|
+ buf = malloc(nand->erasesize);
|
|
|
|
+ if (buf == NULL) {
|
|
|
|
+ puts("Out of memory for erase block buffer\n");
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < patt_count; i++) {
|
|
|
|
+ err = nand->erase(nand, &instr);
|
|
|
|
+ if (err) {
|
|
|
|
+ printf("%s: erase() failed for block at 0x%llx: %d\n",
|
|
|
|
+ nand->name, instr.addr, err);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Make sure the block contains only 0xff bytes */
|
|
|
|
+ err = nand->read(nand, offset, nand->erasesize, &retlen, buf);
|
|
|
|
+ if ((err && err != -EUCLEAN) || retlen != nand->erasesize) {
|
|
|
|
+ printf("%s: read() failed for block at 0x%llx: %d\n",
|
|
|
|
+ nand->name, instr.addr, err);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = check_pattern(buf, 0xff, nand->erasesize);
|
|
|
|
+ if (!err) {
|
|
|
|
+ printf("Erased block at 0x%llx, but a non-0xff byte was found\n",
|
|
|
|
+ offset);
|
|
|
|
+ ret = -EIO;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Write a pattern and check it */
|
|
|
|
+ memset(buf, patterns[i], nand->erasesize);
|
|
|
|
+ err = nand->write(nand, offset, nand->erasesize, &retlen, buf);
|
|
|
|
+ if (err || retlen != nand->erasesize) {
|
|
|
|
+ printf("%s: write() failed for block at 0x%llx: %d\n",
|
|
|
|
+ nand->name, instr.addr, err);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = nand->read(nand, offset, nand->erasesize, &retlen, buf);
|
|
|
|
+ if ((err && err != -EUCLEAN) || retlen != nand->erasesize) {
|
|
|
|
+ printf("%s: read() failed for block at 0x%llx: %d\n",
|
|
|
|
+ nand->name, instr.addr, err);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = check_pattern(buf, patterns[i], nand->erasesize);
|
|
|
|
+ if (!err) {
|
|
|
|
+ printf("Pattern 0x%.2x checking failed for block at "
|
|
|
|
+ "0x%llx\n", patterns[i], offset);
|
|
|
|
+ ret = -EIO;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = 0;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ free(buf);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#endif
|