From bd74280d6218c7bd09021038c92aa07e8a118cc1 Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Mon, 5 Nov 2012 10:15:46 +0000 Subject: nand: Clean up nand_util MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch cleans up nand_util.c: - Fix tabs. - Fix typos. - Remove space character before opening parenthesis in function calls. - Fix comments. Signed-off-by: Benoît Thébaudeau Cc: Scott Wood --- drivers/mtd/nand/nand_util.c | 64 ++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'drivers/mtd/nand/nand_util.c') diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index c4752a7cb..3f111038f 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -50,8 +50,8 @@ #include #include -typedef struct erase_info erase_info_t; -typedef struct mtd_info mtd_info_t; +typedef struct erase_info erase_info_t; +typedef struct mtd_info mtd_info_t; /* support only for native endian JFFS2 */ #define cpu_to_je16(x) (x) @@ -59,7 +59,7 @@ typedef struct mtd_info mtd_info_t; /** * nand_erase_opts: - erase NAND flash with support for various options - * (jffs2 formating) + * (jffs2 formatting) * * @param meminfo NAND device to erase * @param opts options, @see struct nand_erase_options @@ -81,7 +81,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) struct nand_chip *chip = meminfo->priv; if ((opts->offset & (meminfo->writesize - 1)) != 0) { - printf("Attempt to erase non page aligned data\n"); + printf("Attempt to erase non page-aligned data\n"); return -1; } @@ -94,8 +94,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) erase_length = lldiv(opts->length + meminfo->erasesize - 1, meminfo->erasesize); - cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); cleanmarker.totlen = cpu_to_je32(8); /* scrub option allows to erase badblock. To prevent internal @@ -118,7 +118,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) erased_length < erase_length; erase.addr += meminfo->erasesize) { - WATCHDOG_RESET (); + WATCHDOG_RESET(); if (!opts->scrub && bbtest) { int ret = meminfo->block_isbad(meminfo, erase.addr); @@ -259,7 +259,7 @@ int nand_lock(struct mtd_info *mtd, int tight) * flash * * @param mtd nand mtd instance - * @param offset page address to query (muss be page aligned!) + * @param offset page address to query (must be page-aligned!) * * @return -1 in case of error * >0 lock status: @@ -281,7 +281,7 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) if ((offset & (mtd->writesize - 1)) != 0) { - printf ("nand_get_lock_status: " + printf("nand_get_lock_status: " "Start address must be beginning of " "nand page!\n"); ret = -1; @@ -332,20 +332,20 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length, /* check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) { - printf ("nand_unlock: Device is write protected!\n"); + printf("nand_unlock: Device is write protected!\n"); ret = -1; goto out; } if ((start & (mtd->erasesize - 1)) != 0) { - printf ("nand_unlock: Start address must be beginning of " + printf("nand_unlock: Start address must be beginning of " "nand block!\n"); ret = -1; goto out; } if (length == 0 || (length & (mtd->erasesize - 1)) != 0) { - printf ("nand_unlock: Length must be a multiple of nand block " + printf("nand_unlock: Length must be a multiple of nand block " "size %08x!\n", mtd->erasesize); ret = -1; goto out; @@ -485,7 +485,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, pages = nand->erasesize / nand->writesize; blocksize = (pages * nand->oobsize) + nand->erasesize; if (*length % (nand->writesize + nand->oobsize)) { - printf ("Attempt to write incomplete page" + printf("Attempt to write incomplete page" " in yaffs mode\n"); return -EINVAL; } @@ -507,25 +507,25 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, * partition boundary). So don't try to handle that. */ if ((offset & (nand->writesize - 1)) != 0) { - printf ("Attempt to write non page aligned data\n"); + printf("Attempt to write non page-aligned data\n"); *length = 0; return -EINVAL; } need_skip = check_skip_len(nand, offset, *length); if (need_skip < 0) { - printf ("Attempt to write outside the flash area\n"); + printf("Attempt to write outside the flash area\n"); *length = 0; return -EINVAL; } if (!need_skip && !(flags & WITH_DROP_FFS)) { - rval = nand_write (nand, offset, length, buffer); + rval = nand_write(nand, offset, length, buffer); if (rval == 0) return 0; *length = 0; - printf ("NAND write to offset %llx failed %d\n", + printf("NAND write to offset %llx failed %d\n", offset, rval); return rval; } @@ -534,10 +534,10 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, size_t block_offset = offset & (nand->erasesize - 1); size_t write_size, truncated_write_size; - WATCHDOG_RESET (); + WATCHDOG_RESET(); - if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { - printf ("Skip bad block 0x%08llx\n", + if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) { + printf("Skip bad block 0x%08llx\n", offset & ~(nand->erasesize - 1)); offset += nand->erasesize - block_offset; continue; @@ -592,7 +592,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, } if (rval != 0) { - printf ("NAND write to offset %llx failed %d\n", + printf("NAND write to offset %llx failed %d\n", offset, rval); *length -= left_to_write; return rval; @@ -608,13 +608,13 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, * nand_read_skip_bad: * * Read image from NAND flash. - * Blocks that are marked bad are skipped and the next block is readen + * Blocks that are marked bad are skipped and the next block is read * instead as long as the image is short enough to fit even after skipping the * bad blocks. * * @param nand NAND device * @param offset offset in flash - * @param length buffer length, on return holds remaining bytes to read + * @param length buffer length, on return holds number of read bytes * @param buffer buffer to write to * @return 0 in case of success */ @@ -627,25 +627,25 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, int need_skip; if ((offset & (nand->writesize - 1)) != 0) { - printf ("Attempt to read non page aligned data\n"); + printf("Attempt to read non page-aligned data\n"); *length = 0; return -EINVAL; } need_skip = check_skip_len(nand, offset, *length); if (need_skip < 0) { - printf ("Attempt to read outside the flash area\n"); + printf("Attempt to read outside the flash area\n"); *length = 0; return -EINVAL; } if (!need_skip) { - rval = nand_read (nand, offset, length, buffer); + rval = nand_read(nand, offset, length, buffer); if (!rval || rval == -EUCLEAN) return 0; *length = 0; - printf ("NAND read from offset %llx failed %d\n", + printf("NAND read from offset %llx failed %d\n", offset, rval); return rval; } @@ -654,10 +654,10 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, size_t block_offset = offset & (nand->erasesize - 1); size_t read_length; - WATCHDOG_RESET (); + WATCHDOG_RESET(); - if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { - printf ("Skipping bad block 0x%08llx\n", + if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) { + printf("Skipping bad block 0x%08llx\n", offset & ~(nand->erasesize - 1)); offset += nand->erasesize - block_offset; continue; @@ -668,9 +668,9 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, else read_length = nand->erasesize - block_offset; - rval = nand_read (nand, offset, &read_length, p_buffer); + rval = nand_read(nand, offset, &read_length, p_buffer); if (rval && rval != -EUCLEAN) { - printf ("NAND read from offset %llx failed %d\n", + printf("NAND read from offset %llx failed %d\n", offset, rval); *length -= left_to_read; return rval; -- cgit v1.2.3-70-g09d2 From 8156f732ee49fc4d91aef1ce09fb6679b3a23c0c Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Mon, 5 Nov 2012 10:16:15 +0000 Subject: nand: Fix nand_erase_opts() offset check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NAND Flash is erased by blocks, not by pages. Signed-off-by: Benoît Thébaudeau Cc: Scott Wood --- drivers/mtd/nand/nand_util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/nand/nand_util.c') diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 3f111038f..285568316 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -80,8 +80,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) struct mtd_oob_ops oob_opts; struct nand_chip *chip = meminfo->priv; - if ((opts->offset & (meminfo->writesize - 1)) != 0) { - printf("Attempt to erase non page-aligned data\n"); + if ((opts->offset & (meminfo->erasesize - 1)) != 0) { + printf("Attempt to erase non block-aligned data\n"); return -1; } -- cgit v1.2.3-70-g09d2 From 3287f6d3858faee768a7c47515bd21914ad591a2 Mon Sep 17 00:00:00 2001 From: Benoît Thébaudeau Date: Fri, 16 Nov 2012 20:20:54 +0100 Subject: nand: Add torture feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a NAND Flash torture feature, which is useful as a block stress test to determine if a block is still good and reliable (or should be marked as bad), e.g. after a write error. This code is ported from mtd-utils' lib/libmtd.c. Signed-off-by: Benoît Thébaudeau Cc: Scott Wood [scottwood@freescale.com: removed unnec. ifdef and unwrapped error strings] Signed-off-by: Scott Wood --- common/cmd_nand.c | 22 ++++++++ doc/README.nand | 21 ++++++++ drivers/mtd/nand/nand_util.c | 122 +++++++++++++++++++++++++++++++++++++++++++ include/nand.h | 1 + 4 files changed, 166 insertions(+) (limited to 'drivers/mtd/nand/nand_util.c') diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 4b1606972..1568594ca 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -700,6 +700,25 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 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; @@ -810,6 +829,9 @@ static char nand_help_text[] = "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" diff --git a/doc/README.nand b/doc/README.nand index c13018958..a1a511c53 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -108,6 +108,9 @@ Configuration Options: 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. @@ -213,6 +216,24 @@ Miscellaneous and testing commands: 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) diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 285568316..2ba0c5ef9 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -683,3 +683,125 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, 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 diff --git a/include/nand.h b/include/nand.h index bbe28b20b..dded4e27f 100644 --- a/include/nand.h +++ b/include/nand.h @@ -139,6 +139,7 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, 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); #define NAND_LOCK_STATUS_TIGHT 0x01 #define NAND_LOCK_STATUS_UNLOCK 0x04 -- cgit v1.2.3-70-g09d2