return ret == 0 ? 0 : 1;
}
+#ifdef CONFIG_CMD_NAND_TORTURE
+ if (strcmp(cmd, "torture") == 0) {
+ if (argc < 3)
+ goto usage;
+
+ if (!str2off(argv[2], &off)) {
+ puts("Offset is not a valid number\n");
+ return 1;
+ }
+
+ printf("\nNAND torture: device %d offset 0x%llx size 0x%x\n",
+ dev, off, nand->erasesize);
+ ret = nand_torture(nand, off);
+ printf(" %s\n", ret ? "Failed" : "Passed");
+
+ return ret == 0 ? 0 : 1;
+ }
+#endif
+
if (strcmp(cmd, "markbad") == 0) {
argc -= 2;
argv += 2;
"nand erase.chip [clean] - erase entire chip'\n"
"nand bad - show bad blocks\n"
"nand dump[.oob] off - dump page\n"
+#ifdef CONFIG_CMD_NAND_TORTURE
+ "nand torture off - torture block at offset\n"
+#endif
"nand scrub [-y] off size | scrub.part partition | scrub.chip\n"
" really clean NAND erasing bad blocks (UNSAFE)\n"
"nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n"
CONFIG_CMD_NAND
Enables NAND support and commmands.
+ CONFIG_CMD_NAND_TORTURE
+ Enables the torture command (see description of this command below).
+
CONFIG_MTD_NAND_ECC_JFFS2
Define this if you want the Error Correction Code information in
the out-of-band data to be formatted to match the JFFS2 file system.
DANGEROUS!!! Factory set bad blocks will be lost. Use only
to remove artificial bad blocks created with the "markbad" command.
+ "torture offset"
+ Torture block to determine if it is still reliable.
+ Enabled by the CONFIG_CMD_NAND_TORTURE configuration option.
+ This command returns 0 if the block is still reliable, else 1.
+ If the block is detected as unreliable, it is up to the user to decide to
+ mark this block as bad.
+ The analyzed block is put through 3 erase / write cycles (or less if the block
+ is detected as unreliable earlier).
+ This command can be used in scripts, e.g. together with the markbad command to
+ automate retries and handling of possibly newly detected bad blocks if the
+ nand write command fails.
+ It can also be used manually by users having seen some NAND errors in logs to
+ search the root cause of these errors.
+ The underlying nand_torture() function is also useful for code willing to
+ automate actions following a nand->write() error. This would e.g. be required
+ in order to program or update safely firmware to NAND, especially for the UBI
+ part of such firmware.
+
NAND locking command (for chips with active LOCKPRE pin)
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