Skip to contentSkip to conversation

NAND read/write.jffs2 fix

PATCH New Morten Ebbell Hestens 3 participants 6 messages May 13, 2008 – May 16, 2008

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 NAND read/write.jffs2 fix

patch for branch mtd-2.6.22.1 on git://git.denx.de/u-boot-nand-flash.git

nand read(.jffs2|.e|.i) skips bad blocks during read. write(.jffs2|.e|.i) skips bad blocks during write nand read will read 0xff for bad block. Update documentation.

---
 common/cmd_nand.c            |   77 ++---
 doc/README.nand              |   16 +-
 drivers/mtd/nand/nand_util.c |  813 ++++++++++++------------------------------
 include/nand.h               |    6 +-
 4 files changed, 272 insertions(+), 640 deletions(-)
common/cmd_nand.c +29 -48
index eff9173..237f4c9 100644
@@ -39,6 +39,7 @@ int find_dev_and_part(const char *id, struct mtd_device **dev,
 #endif
 
 extern nand_info_t nand_info[];       /* info for NAND chips */
+extern struct nand_chip nand_chip[];  /* extra info for NAND chips */
 
 static int nand_dump(nand_info_t *nand, ulong off, int only_oob)
 {
@@ -73,9 +74,9 @@ static int nand_dump(nand_info_t *nand, ulong off, int only_oob)
 	while (i--) {
 		if (!only_oob) {
 			printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x"
-					"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
-					p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
-					p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+				"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
+				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
 		}
 		p += 16;
 	}
@@ -319,7 +320,6 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 
 	}
 
-	/* read write */
 	if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
 		int read;
 
@@ -327,40 +327,19 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 			goto usage;
 
 		addr = (ulong)simple_strtoul(argv[2], NULL, 16);
-
 		read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
 		printf("\nNAND %s: ", read ? "read" : "write");
 		if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
 			return 1;
 
 		s = strchr(cmd, '.');
-		if (s != NULL &&
-		    (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) {
-			if (read) {
-				/* read */
-				nand_read_options_t opts;
-				memset(&opts, 0, sizeof(opts));
-				opts.buffer	= (u_char*) addr;
-				opts.length	= size;
-				opts.offset	= off;
-				opts.quiet      = quiet;
-/*
- *  ! BROKEN !
- *
- *  TODO: Function must be implemented
- *
- *				ret = nand_read_opts(nand, &opts);
- */
-			} else {
-				/* write */
-				mtd_oob_ops_t opts;
-				memset(&opts, 0, sizeof(opts));
-				opts.datbuf	= (u_char*) addr;
-				opts.len	= size;
-				opts.ooblen	= 64;
-				opts.mode	= MTD_OOB_AUTO;
-				ret = nand_write_opts(nand, off, &opts);
-			}
+		if (s != NULL && (!strcmp(s, ".jffs2") ||
+				  !strcmp(s, ".e") || !strcmp(s, ".i"))) {
+			if (read)
+				ret = nand_read_skip_bad(nand, off, &size, (u_char *)addr);
+			else
+				ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr);
+
 		} else if (s != NULL && !strcmp(s, ".oob")) {
 			struct mtd_oob_ops ops;
 
@@ -372,9 +351,10 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 				ret = nand->read_oob(nand, off, &ops);
 			else
 				ret = nand->write_oob(nand, off, &ops);
+
 		} else {
 			if (read)
-				ret = nand_read(nand, off, &size, (u_char *)addr);
+				ret = nand_read_ff_upon_bad(nand, off, &size,(u_char *)addr);
 			else
 				ret = nand_write(nand, off, &size, (u_char *)addr);
 		}
@@ -399,6 +379,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 		}
 		return 1;
 	}
+
 	if (strcmp(cmd, "biterr") == 0) {
 		/* todo */
 		return 1;
@@ -490,21 +471,21 @@ usage:
 }
 
 U_BOOT_CMD(nand, 5, 1, do_nand,
-	"nand    - NAND sub-system\n",
-	"info                  - show available NAND devices\n"
-	"nand device [dev]     - show or set current device\n"
-	"nand read[.jffs2]     - addr off|partition size\n"
-	"nand write[.jffs2]    - addr off|partition size - read/write `size' bytes starting\n"
-	"    at offset `off' to/from memory address `addr'\n"
-	"nand erase [clean] [off size] - erase `size' bytes from\n"
-	"    offset `off' (entire device if not specified)\n"
-	"nand bad - show bad blocks\n"
-	"nand dump[.oob] off - dump page\n"
-	"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
-	"nand markbad off - mark bad block at offset (UNSAFE)\n"
-	"nand biterr off - make a bit error at offset (UNSAFE)\n"
-	"nand lock [tight] [status] - bring nand to lock state or display locked pages\n"
-	"nand unlock [offset] [size] - unlock section\n");
+	   "nand    - NAND sub-system\n",
+	   "info              - show available NAND devices\n"
+	   "nand device [dev]      - show or set current device\n"
+	   "nand read[.jffs2, .i]  addr off|partition size\n"
+	   "nand write[.jffs2, .i] addr off|partition size\n"
+	   "  - read/write `size' bytes starting at offset `off' to/from memory address `addr'\n"
+	   "nand erase [clean] [off size]\n"
+	   "  - erase `size' bytes from offset `off' (entire device if not specified)\n"
+	   "nand bad               - show bad blocks\n"
+	   "nand dump[.oob] off    - dump page\n"
+	   "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
+	   "nand markbad off - mark bad block at offset (UNSAFE)\n"
+	   "nand biterr off  - make a bit error at offset (UNSAFE)\n"
+	   "nand lock [tight] [status]  - bring nand to lock state or display locked pages\n"
+	   "nand unlock [offset] [size] - unlock section\n");
 
 static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
 			   ulong offset, ulong addr, char *cmd)
doc/README.nand +8 -8
index 647a6b8..56f791f 100644
@@ -57,14 +57,14 @@ Commands:
       Print information about all of the NAND devices found.
 
    nand read addr ofs|partition size
-      Read `size' bytes from `ofs' in NAND flash to `addr'. If a page
-      cannot be read because it is marked bad or an uncorrectable data
-      error is found the command stops with an error.
+      Read `size' bytes from `ofs' in NAND flash to `addr'. Data for
+      blocks that are marked bad is read as 0xff. If an uncorrectable 
+      data error is found the command stops with an error.
 
-   nand read.jffs2 addr ofs|partition size
-      Like `read', but the data for blocks that are marked bad is read as
-      0xff. This gives a readable JFFS2 image that can be processed by
-      the JFFS2 commands such as ls and fsload.
+   nand read.jffs2|.i addr ofs|partition size
+      Like `read', but the data for blocks that are marked bad are skipped
+      and it read the next block instead. This gives a readable JFFS2 image
+      that can be processed by the JFFS2 commands such as ls and fsload.
 
    nand read.oob addr ofs|partition size
       Read `size' bytes from the out-of-band data area corresponding to
@@ -77,7 +77,7 @@ Commands:
       cannot be written because it is marked bad or the write fails the
       command stops with an error.
 
-   nand write.jffs2 addr ofs|partition size
+   nand write.jffs2|.i addr ofs|partition size
       Like `write', but blocks that are marked bad are skipped and the
       data is written to the next block instead. This allows writing
       a JFFS2 image, as long as the image is short enough to fit even
drivers/mtd/nand/nand_util.c +232 -581

828 lines not on this page · raw message

include/nand.h +3 -3
index 67dedbe..2388e7e 100644
@@ -106,9 +106,9 @@ struct nand_erase_options {
 
 typedef struct nand_erase_options nand_erase_options_t;
 
-int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops);
-
-int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts);
+int nand_read_ff_upon_bad(nand_info_t *nand, ulong offset, ulong *length, u_char *buffer);
+int nand_read_skip_bad(nand_info_t *nand, ulong offset, ulong *length, u_char *buffer);
+int nand_write_skip_bad(nand_info_t *nand, ulong offset, ulong *length, u_char *buffer);
 int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts);
 
 #define NAND_LOCK_STATUS_TIGHT	0x01
Signature · 1 line
1.5.2.5
#1 lore raw

PATCH 1/1 NAND read/write.jffs2 fix

Quoted 2 lines · Morten Ebbell Hestens

extern nand_info_t nand_info[]; /* info for NAND chips */
+extern struct nand_chip nand_chip[]; /* extra info for NAND chips */

Where is this defined or used?

common/cmd_nand.c static int nand_dump(nand_info_t *nand, ulong off, int only_oob) · line 79

Quoted 13 lines

static int nand_dump(nand_info_t *nand, ulong off, int only_oob) {

common/cmd_nand.c +3 -3
@@ -73,9 +74,9 @@ static int nand_dump(nand_info_t *nand, ulong off, int only_oob)
 	while (i--) {
 		if (!only_oob) {
 			printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x"
-					"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
-					p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
-					p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+				"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
+				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);

Still exceeds 80 characters on the last line.

common/cmd_nand.c int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) · line 333

Quoted 9 lines
common/cmd_nand.c +0 -1
@@ -327,40 +327,19 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 			goto usage;
 
 		addr = (ulong)simple_strtoul(argv[2], NULL, 16);
-
 		read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
 		printf("\nNAND %s: ", read ? "read" : "write");
 		if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
 			return 1;

What's wrong with that newline?

common/cmd_nand.c int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) · line 355

Quoted 6 lines
common/cmd_nand.c +1 -0
@@ -372,9 +351,10 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 				ret = nand->read_oob(nand, off, &ops);
 			else
 				ret = nand->write_oob(nand, off, &ops);
+
 		} else {

Why add a blank line here?

Quoted 9 lines
common/cmd_nand.c +0 -8
 U_BOOT_CMD(nand, 5, 1, do_nand,
-	"nand    - NAND sub-system\n",
-	"info                  - show available NAND devices\n"
-	"nand device [dev]     - show or set current device\n"
-	"nand read[.jffs2]     - addr off|partition size\n"
-	"nand write[.jffs2]    - addr off|partition size - read/write `size' bytes starting\n"
-	"    at offset `off' to/from memory address `addr'\n"
-	"nand erase [clean] [off size] - erase `size' bytes from\n"
-	"    offset `off' (entire device if not specified)\n"

common/cmd_nand.c usage: · line 476

Quoted 10 lines
common/cmd_nand.c +3 -7
-	"nand bad - show bad blocks\n"
-	"nand dump[.oob] off - dump page\n"
-	"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
-	"nand markbad off - mark bad block at offset (UNSAFE)\n"
-	"nand biterr off - make a bit error at offset (UNSAFE)\n"
-	"nand lock [tight] [status] - bring nand to lock state or display locked pages\n"
-	"nand unlock [offset] [size] - unlock section\n");
+	   "nand    - NAND sub-system\n",
+	   "info              - show available NAND devices\n"
+	   "nand device [dev]      - show or set current device\n"

Should either keep these lined up or consistently have only one character before the dash.

common/cmd_nand.c usage: · line 478

Quoted 2 lines
common/cmd_nand.c +2 -0
+	   "nand read[.jffs2, .i]  addr off|partition size\n"
+	   "nand write[.jffs2, .i] addr off|partition size\n"

What about .e? Is it just for backwards compatibility that we have three commands that mean the same thing? Do we want to document all three?

common/cmd_nand.c usage: · line 479

Quoted 1 line
common/cmd_nand.c +1 -0
+	   "  - read/write `size' bytes starting at offset `off' to/from memory address `addr'\n"

Maybe mention the effect of .jffs2/.i/.e here in addition to the offline documentation.

Quoted 5 lines
drivers/mtd/nand/nand_util.c +0 -0
index 1916aca..dd4ed6b 100644

drivers/mtd/nand/nand_util.c typedef struct mtd_info mtd_info_t; · line 61

Quoted 10 lines
drivers/mtd/nand/nand_util.c +7 -0
@@ -52,6 +52,14 @@ typedef struct mtd_info	  mtd_info_t;
 #define cpu_to_je16(x) (x)
 #define cpu_to_je32(x) (x)
 
+#if 0
+#define MARK printf("Was here: %s, %s, %d\n",__FILE__, __FUNCTION__, __LINE__);
+#define DEBUGP(fmt,args...) printf("%s, %s, %d: "fmt,__FILE__, __FUNCTION__, __LINE__,##args)
+#else
+#define MARK
+#define DEBUGP(fmt,args...)
+#endif

We should probably use the existing MTD DEBUG() facility.

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 445

Quoted 8 lines
drivers/mtd/nand/nand_util.c +8 -0
+/**
+ * get_len_incl_bad
+ *
+ * Check if length including bad blocks fits into device.
+ *
+ * @param nand  	NAND device
+ * @param offset 	offset in flash
+ * @param len		image lenght

length

Quoted 3 lines

+ * @return image length including bad blocks
+ */
+static ulong get_len_incl_bad (nand_info_t *nand, ulong offset, ulong *len)

Why is len a pointer? It doesn't seem to be modified.

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 457

Quoted 9 lines
drivers/mtd/nand/nand_util.c +9 -0
+{
+	ulong   len_incl_bad;
+	ulong   len_excl_bad;
+	ulong   block_len;
+
+	DEBUGP ("*len %05x, nand->erasesize %05x\n", *len, nand->erasesize);
+
+	for (len_excl_bad = 0, len_incl_bad = 0;  len_excl_bad < *len;  )
+	{

Brace on preceding line. Might be clearer as a while statement.

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 461

Quoted 4 lines
drivers/mtd/nand/nand_util.c +4 -0
+		block_len = nand->erasesize - (offset & (nand->erasesize - 1));
+
+		if (!nand_block_isbad (nand, offset))
+			len_excl_bad += block_len;

Hmm, is it safe to call nand_block_isbad with an offset that isn't the start of the block? The BBT implementation seems OK with it, but what about block_bad callbacks?

Quoted 11 lines

+/**
+ * nand_write_skip_bad:
+ *
+ * Write image to NAND flash.
+ * Blocks that are marked bad are skipped and the is written to the next
+ * block 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 len buffer lenght

length

Quoted 18 lines

+ * @param buf buffer to read from
+ * @return 0 in case of success
+ */

drivers/mtd/nand/nand_util.c +15 -0
+int nand_write_skip_bad(nand_info_t *nand, ulong offset, ulong *len, u_char *buf)
+{
+	int     result;
+	u_char *p_buffer = buf;
+	ulong   left_to_write = *len;
+	ulong   len_incl_bad;
+
+	DEBUGP ("offset %08x, len  0x%05x\n", offset, *len);
+
+	/* Reject writes, which are not page aligned */
+	if (((offset & (nand->writesize - 1)) != 0) || 
+	    ((*len & (nand->writesize - 1)) != 0)) {
+		printf ("Attempt to write not page aligned data\n");
+		return -EINVAL;
+	}

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 513

Quoted 10 lines
drivers/mtd/nand/nand_util.c +10 -0
+
+	len_incl_bad = get_len_incl_bad (nand, offset, len); 
+
+	if ((offset + len_incl_bad) > nand->size) {
+		printf ("Attempt to write outside the flash area\n");
+		return -EINVAL;
+	}
+	
+	if (len_incl_bad == *len)
+		return (nand_write (nand, offset, len, buf));

Unnecessary parentheses.

Here, len will be modified to show how much was written on an error, but in the loop below, it won't. In the loop below, we'll print an error if the write fails, but here we won't.

Quoted 13 lines
drivers/mtd/nand/nand_util.c +13 -0
+	while (left_to_write > 0) {
+		ulong block_offset = offset & (nand->erasesize - 1);
+		ulong write_size;
+
+		DEBUGP ("left %05x, offset %08x, block_off %05x, p_buf %08x\n",
+			left_to_write, offset, block_offset, p_buffer);
+
+		if (nand_block_isbad (nand, offset)) {
+			printf ("Skip bad block 0x%08x\n",
+				offset & ~(nand->erasesize - 1));
+			offset += nand->erasesize - block_offset;
+			continue;
+		}

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 537

Quoted 10 lines
drivers/mtd/nand/nand_util.c +10 -0
+
+		if (left_to_write < (nand->erasesize - block_offset))
+			write_size = left_to_write;
+		else
+			write_size = nand->erasesize - block_offset;
+
+		result = nand_write (nand, offset, &write_size, p_buffer);
+		if (result != 0) {
+			printf ("Write failed %d\n", result);
+			return result;

We should probably be more detailed about exactly what failed. It's obvious (except for the exact block that failed) if the user typed in the command directly, but not if it's from a script.

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 541

Quoted 4 lines
drivers/mtd/nand/nand_util.c +4 -0
+		}		
+		left_to_write -= write_size;
+		offset        += write_size;
+		p_buffer = (u_char *)((ulong)p_buffer + write_size);

Why not just p_buffer += write_size?

Quoted 4 lines

+/**
+ * nand_read_ff_upon_bad:
+ *
+ * Read image from NAND flash.

Quoted 8 lines

+ * Data from blocks that are marked bad is read as 0xff.
+ *
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @param len buffer length
+ * @param buf buffer to write to
+ * @return 0 in case of success
+ */

What was the reason for changing the behavior of non-jffs2 reads, BTW?

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 626

Quoted 5 lines
drivers/mtd/nand/nand_util.c +5 -0
+int nand_read_ff_upon_bad (nand_info_t *nand, ulong offset, ulong *len, u_char *buf)
+{
+	int     result;
+	u_char *p_buffer = buf;
+	ulong   left_to_read = *len;

The aligned variable declaration style can be annoying to maintain if a new variable with a longer-named type has to be added in the future...

Quoted 10 lines
drivers/mtd/nand/nand_util.c +10 -0
+	while (left_to_read > 0) {
+		ulong block_offset = offset & (nand->erasesize - 1);
+		ulong read_length;
+
+		DEBUGP ("left %05x, offset %08x, block_off %04x, p_buf %08x\n",
+			left_to_read, offset, block_offset, p_buffer);
+
+		if (left_to_read < (nand->erasesize - block_offset))
+			read_length = left_to_read;
+		else

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 647

Quoted 10 lines
drivers/mtd/nand/nand_util.c +10 -0
+			read_length = nand->erasesize - block_offset;
+		
+		if (nand_block_isbad (nand, offset)) {
+			int i;
+			printf ("Bad block 0x%08x. Read this block as 0xff\n",
+				offset & ~(nand->erasesize - 1));
+			for (i = 0;  i < read_length;  i++)
+				p_buffer[i] = 0xff;
+
+		} else {

No blank line at the end of the block.

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 654

Quoted 7 lines
drivers/mtd/nand/nand_util.c +7 -0
+			result = nand_read (nand, offset, &read_length, p_buffer);
+			if (result != 0) {
+				printf ("Read failed %d\n", result);
+				return result;
+			}		
+		}
+		left_to_read -= read_length;

A blank line after the end of a block would be nice, though.

-Scott

#2 lore raw

PATCH 1/1 NAND read/write.jffs2 fix

Quoted 16 lines · On Tue, 13 May 2008 15:41:06 -0500 Scott Wood <[email protected]> wrote:
Quoted 5 lines

static int nand_dump(nand_info_t *nand, ulong off, int only_oob) {

common/cmd_nand.c static int nand_dump(nand_info_t *nand, ulong off, int only_oob) · line 79

Quoted 9 lines
common/cmd_nand.c +3 -3
@@ -73,9 +74,9 @@ static int nand_dump(nand_info_t *nand, ulong off, int only_oob)
 	while (i--) {
 		if (!only_oob) {
 			printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x"
-					"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
-					p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
-					p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+				"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
+				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);

Still exceeds 80 characters on the last line.

or use lib_generic/display_options.c:print_buffer()

Kim

#3 lore raw

PATCH 1/1 NAND read/write.jffs2 fix

Hi Kim,

Quoted 18 lines · Kim Phillips
Quoted 14 lines · On Tue, 13 May 2008 15:41:06 -0500 Scott Wood <[email protected]> wrote:

common/cmd_nand.c static int nand_dump(nand_info_t *nand, ulong off, int only_oob) · line 79

Quoted 13 lines

static int nand_dump(nand_info_t *nand, ulong off, int only_oob) {

common/cmd_nand.c +3 -3
@@ -73,9 +74,9 @@ static int nand_dump(nand_info_t *nand, ulong off, int only_oob)
 	while (i--) {
 		if (!only_oob) {
 			printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x"
-					"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
-					p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
-					p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+				"  %02x %02x %02x %02x %02x %02x %02x %02x\n",
+				p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
+				p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);

Still exceeds 80 characters on the last line.

Quoted 1 line

or use lib_generic/display_options.c:print_buffer()

806b4da8: ff ff 01 10 00 00 05 01 ........
or

        ff ff 01 10 00 00 05 01

The 806b4da8 is a address RAM address and has nothing to do with the nand offset. I think the last printout is better.

Morten

#4 lore raw

PATCH 1/1 NAND read/write.jffs2 fix

In reply to Scott Wood

Hi Scott,

Thanks for a good review !
See my comment below.
I will later make av new version of this patch.

Quoted 4 lines · Scott Wood
Quoted 2 lines

extern nand_info_t nand_info[]; /* info for NAND chips */
+extern struct nand_chip nand_chip[]; /* extra info for NAND chips */

Where is this defined or used?

No, it's for a other patch. I will remove it.

Quoted 4 lines
Quoted 4 lines
common/cmd_nand.c +0 -0
@@ -327,40 +327,19 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int 
 argc, char *argv[])
             goto usage;
 
Quoted 8 lines

common/cmd_nand.c int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) · line 333

Quoted 6 lines
common/cmd_nand.c +0 -1
         addr = (ulong)simple_strtoul(argv[2], NULL, 16);
-
         read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
         printf("\nNAND %s: ", read ? "read" : "write");
         if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
             return 1;

What's wrong with that newline?

I will revert to get a less noisy patch.

Quoted 9 lines

common/cmd_nand.c int do_nand(cmd_tbl_t * cmdtp, int flag, int · line 356

Quoted 7 lines
common/cmd_nand.c +1 -0
@@ -372,9 +351,10 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int 
 argc, char *argv[])
                 ret = nand->read_oob(nand, off, &ops);
             else
                 ret = nand->write_oob(nand, off, &ops);
+
         } else {

Why add a blank line here?

I will revert.

Quoted 22 lines
Quoted 17 lines
common/cmd_nand.c +0 -5
 U_BOOT_CMD(nand, 5, 1, do_nand,
-    "nand    - NAND sub-system\n",
-    "info                  - show available NAND devices\n"
-    "nand device [dev]     - show or set current device\n"
-    "nand read[.jffs2]     - addr off|partition size\n"
-    "nand write[.jffs2]    - addr off|partition size - read/write 

`size' bytes starting\n"

common/cmd_nand.c +0 -9
-    "    at offset `off' to/from memory address `addr'\n"
-    "nand erase [clean] [off size] - erase `size' bytes from\n"
-    "    offset `off' (entire device if not specified)\n"
-    "nand bad - show bad blocks\n"
-    "nand dump[.oob] off - dump page\n"
-    "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
-    "nand markbad off - mark bad block at offset (UNSAFE)\n"
-    "nand biterr off - make a bit error at offset (UNSAFE)\n"
-    "nand lock [tight] [status] - bring nand to lock state or display 

locked pages\n"

common/cmd_nand.c usage: · line 476

Quoted 4 lines
common/cmd_nand.c +3 -1
-    "nand unlock [offset] [size] - unlock section\n");
+       "nand    - NAND sub-system\n",
+       "info              - show available NAND devices\n"
+       "nand device [dev]      - show or set current device\n"
Quoted 2 lines

Should either keep these lined up or consistently have only one character before the dash.

Ok.

Quoted 5 lines

common/cmd_nand.c usage: · line 478

Quoted 2 lines
common/cmd_nand.c +2 -0
+       "nand read[.jffs2, .i]  addr off|partition size\n"
+       "nand write[.jffs2, .i] addr off|partition size\n"

What about .e? Is it just for backwards compatibility that we have three commands that mean the same thing? Do we want to document all three?

The doc/README.nand only mention .jffs2 and .i
The option .jffs2 only has skipping bad blocks (and ECC) in common with the jffs2 file system. If we want to be backwards compatible we should use [.jffs2|.i|.e] otherwise [.i|.e] or [.skip] would be better. I leave this choice to you Scott.

Quoted 5 lines
Quoted 2 lines

+ " - read/write size' bytes starting at offset off' to/from
memory address `addr'\n"

Maybe mention the effect of .jffs2/.i/.e here in addition to the offline documentation.

Agree.
Something like this ?
"nand - NAND sub-system\n",
"info - show available NAND devices\n"
"nand device [dev] - show or set current device\n"
"nand read[.jffs2|.i|.e] addr off|partition size - read size' bytes\n" " starting at offset off' to memory address addr'. Using one of\n" " the .' options bad blocks is skipped otherwise read as 0xff\n" "nand write[.jffs2|.i|.e] addr off|partition size - write size'\n" " bytes starting at offset off' from memory address addr'. Using\n" " one of .' options bad blocks is skipped otherwise write fails\n" "nand erase [clean] [off size] - erase size' bytes from offset\n" " off' (entire device if not specified)\n"
"nand bad - show bad blocks\n"
"nand dump[.oob] off - dump page\n"
"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" "nand markbad off - mark bad block at offset (UNSAFE)\n" "nand biterr off - make a bit error at offset (UNSAFE)\n" "nand lock [tight] [status] - bring nand to lock state or display\n" " locked pages\n"
"nand unlock [offset] [size] - unlock section\n");

Quoted 8 lines
Quoted 8 lines
drivers/mtd/nand/nand_util.c +0 -0
index 1916aca..dd4ed6b 100644
@@ -52,6 +52,14 @@ typedef struct mtd_info      mtd_info_t;
 #define cpu_to_je16(x) (x)
 #define cpu_to_je32(x) (x)
 
Quoted 11 lines

drivers/mtd/nand/nand_util.c typedef struct mtd_info mtd_info_t; · line 61

Quoted 9 lines
drivers/mtd/nand/nand_util.c +7 -0
+#if 0
+#define MARK printf("Was here: %s, %s, %d\n",__FILE__, __FUNCTION__, 
 __LINE__);
+#define DEBUGP(fmt,args...) printf("%s, %s, %d: "fmt,__FILE__, 
 __FUNCTION__, __LINE__,##args)
+#else
+#define MARK
+#define DEBUGP(fmt,args...)
+#endif

We should probably use the existing MTD DEBUG() facility.

I will remove it.

Quoted 6 lines
Quoted 4 lines
drivers/mtd/nand/nand_util.c +3 -0
+ * @return        image length including bad blocks
+ */
+static ulong get_len_incl_bad (nand_info_t *nand, ulong offset, ulong 

*len)

Why is len a pointer? It doesn't seem to be modified.

Will change it to a const ulong length.

Quoted 11 lines

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 457

Quoted 9 lines
drivers/mtd/nand/nand_util.c +9 -0
+{
+    ulong   len_incl_bad;
+    ulong   len_excl_bad;
+    ulong   block_len;
+
+    DEBUGP ("*len %05x, nand->erasesize %05x\n", *len, nand->erasesize);
+
+    for (len_excl_bad = 0, len_incl_bad = 0;  len_excl_bad < *len;  )
+    {

Brace on preceding line. Might be clearer as a while statement.

Agree.

Quoted 8 lines

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 461

Quoted 4 lines
drivers/mtd/nand/nand_util.c +4 -0
+        block_len = nand->erasesize - (offset & (nand->erasesize - 1));
+
+        if (!nand_block_isbad (nand, offset))
+            len_excl_bad += block_len;

Hmm, is it safe to call nand_block_isbad with an offset that isn't the start of the block? The BBT implementation seems OK with it, but what about block_bad callbacks?

Both nand_block_bad and nand_isbad_bbt seems ok.
But just in case it could be changed to "if (!nand_block_isbad (nand, offset & (~nand->erasesize + 1))"

I do not understand what you mean about block_bad callbacks.

Quoted 31 lines
Quoted 18 lines

+ * @param buf buffer to read from
+ * @return 0 in case of success
+ */
+int nand_write_skip_bad(nand_info_t *nand, ulong offset, ulong *len, u_char *buf)

drivers/mtd/nand/nand_util.c +10 -0
+{
+    int     result;
+    u_char *p_buffer = buf;
+    ulong   left_to_write = *len;
+    ulong   len_incl_bad;
+
+    DEBUGP ("offset %08x, len  0x%05x\n", offset, *len);
+
+    /* Reject writes, which are not page aligned */
+    if (((offset & (nand->writesize - 1)) != 0) || +        ((*len & 

(nand->writesize - 1)) != 0)) {

drivers/mtd/nand/nand_util.c +2 -0
+        printf ("Attempt to write not page aligned data\n");
+        return -EINVAL;

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 513

Quoted 10 lines
drivers/mtd/nand/nand_util.c +10 -0
+    }
+
+    len_incl_bad = get_len_incl_bad (nand, offset, len); +
+    if ((offset + len_incl_bad) > nand->size) {
+        printf ("Attempt to write outside the flash area\n");
+        return -EINVAL;
+    }
+   
+    if (len_incl_bad == *len)
+        return (nand_write (nand, offset, len, buf));

Unnecessary parentheses.

Quoted 3 lines

Here, len will be modified to show how much was written on an error, but in the loop below, it won't. In the loop below, we'll print an error if the write fails, but here we won't.

I will fix this.

Quoted 20 lines
Quoted 20 lines
drivers/mtd/nand/nand_util.c +19 -0
+    while (left_to_write > 0) {
+        ulong block_offset = offset & (nand->erasesize - 1);
+        ulong write_size;
+
+        DEBUGP ("left %05x, offset %08x, block_off %05x, p_buf %08x\n",
+            left_to_write, offset, block_offset, p_buffer);
+
+        if (nand_block_isbad (nand, offset)) {
+            printf ("Skip bad block 0x%08x\n",
+                offset & ~(nand->erasesize - 1));
+            offset += nand->erasesize - block_offset;
+            continue;
+        }
+
+        if (left_to_write < (nand->erasesize - block_offset))
+            write_size = left_to_write;
+        else
+            write_size = nand->erasesize - block_offset;
+
Quoted 8 lines

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 537

Quoted 4 lines
drivers/mtd/nand/nand_util.c +4 -0
+        result = nand_write (nand, offset, &write_size, p_buffer);
+        if (result != 0) {
+            printf ("Write failed %d\n", result);
+            return result;

We should probably be more detailed about exactly what failed. It's obvious (except for the exact block that failed) if the user typed in the command directly, but not if it's from a script.

Agree.

Quoted 6 lines

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 541

Quoted 4 lines
drivers/mtd/nand/nand_util.c +4 -0
+        }       
+        left_to_write -= write_size;
+        offset        += write_size;
+        p_buffer = (u_char *)((ulong)p_buffer + write_size);

Why not just p_buffer += write_size?

Agree.

Quoted 12 lines
Quoted 11 lines

+/**
+ * nand_read_ff_upon_bad: + *
+ * Read image from NAND flash. + * Data from blocks that are marked bad is read as 0xff.
+ *
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @param len buffer length
+ * @param buf buffer to write to
+ * @return 0 in case of success
+ */

Quoted 1 line

What was the reason for changing the behavior of non-jffs2 reads, BTW?

Quoting from a conversation with Stefan Roese:

Quoted 13 lines
Quoted 9 lines

In the README.nand:
"
nand read.jffs2 addr ofs|partition size
Like `read', but the data for blocks that are marked bad is read as 0xff. This gives a readable JFFS2 image that can be processed by the JFFS2 commands such as ls and fsload.
"
Is this correct or should read skip the bad blocks and read the next instead ?

No. This seems to be incorrect. IIRC the "normal" nand read read 0xff's upon bad blocks. Please fix this when you submit a new patch.

Quoted 9 lines

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 564

Quoted 6 lines

+int nand_read_ff_upon_bad (nand_info_t *nand, ulong offset, ulong *len, u_char *buf)

drivers/mtd/nand/nand_util.c +4 -0
+{
+    int     result;
+    u_char *p_buffer = buf;
+    ulong   left_to_read = *len;

The aligned variable declaration style can be annoying to maintain if a new variable with a longer-named type has to be added in the future...

Ok.

Quoted 7 lines

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 647

Quoted 5 lines
drivers/mtd/nand/nand_util.c +5 -0
+                offset & ~(nand->erasesize - 1));
+            for (i = 0;  i < read_length;  i++)
+                p_buffer[i] = 0xff;
+
+        } else {

No blank line at the end of the block.

Ok

Quoted 4 lines

drivers/mtd/nand/nand_util.c

Quoted 2 lines
drivers/mtd/nand/nand_util.c +2 -0
+        }
+        left_to_read -= read_length;

A blank line after the end of a block would be nice, though.

Ok

Best regards,
Morten

#5 lore raw

PATCH 1/1 NAND read/write.jffs2 fix

Quoted 7 lines · Morten Ebbell Hestnes
Quoted 6 lines

common/cmd_nand.c usage: · line 478

Quoted 2 lines
common/cmd_nand.c +2 -0
+       "nand read[.jffs2, .i]  addr off|partition size\n"
+       "nand write[.jffs2, .i] addr off|partition size\n"

What about .e? Is it just for backwards compatibility that we have three commands that mean the same thing? Do we want to document all three?

Quoted 6 lines

The doc/README.nand only mention .jffs2 and .i
The option .jffs2 only has skipping bad blocks (and ECC) in common with the jffs2 file system.
If we want to be backwards compatible we should use [.jffs2|.i|.e] otherwise [.i|.e] or [.skip] would be better.
I leave this choice to you Scott.

I think we should stay compatible; I was just curious why some were documented and others weren't.

If we *weren't* maintaining backward compatibility, I'd make the block-skipping mode the default...

Quoted 19 lines
Quoted 5 lines
Quoted 2 lines

+ " - read/write size' bytes starting at offset off' to/from
memory address `addr'\n"

Maybe mention the effect of .jffs2/.i/.e here in addition to the offline documentation.

Agree.
Something like this ?
"nand - NAND sub-system\n",
"info - show available NAND devices\n"
"nand device [dev] - show or set current device\n"
"nand read[.jffs2|.i|.e] addr off|partition size - read size' bytes\n" " starting at offset off' to memory address addr'. Using one of\n" " the .' options bad blocks is skipped otherwise read as 0xff\n" "nand write[.jffs2|.i|.e] addr off|partition size - write size'\n" " bytes starting at offset off' from memory address addr'. Using\n" " one of .' options bad blocks is skipped otherwise write fails\n" "nand erase [clean] [off size] - erase size' bytes from offset\n" " off' (entire device if not specified)\n"

Quoted 8 lines

"nand bad - show bad blocks\n"
"nand dump[.oob] off - dump page\n"
"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" "nand markbad off - mark bad block at offset (UNSAFE)\n" "nand biterr off - make a bit error at offset (UNSAFE)\n" "nand lock [tight] [status] - bring nand to lock state or display\n" " locked pages\n"
"nand unlock [offset] [size] - unlock section\n");

s/blocks is skipped otherwise read/blocks are skipped, otherwise they are read/

Looks OK otherwise.

Quoted 9 lines
Quoted 8 lines

drivers/mtd/nand/nand_util.c int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) · line 461

Quoted 4 lines
drivers/mtd/nand/nand_util.c +4 -0
+        block_len = nand->erasesize - (offset & (nand->erasesize - 1));
+
+        if (!nand_block_isbad (nand, offset))
+            len_excl_bad += block_len;

Hmm, is it safe to call nand_block_isbad with an offset that isn't the start of the block? The BBT implementation seems OK with it, but what about block_bad callbacks?

Quoted 5 lines

Both nand_block_bad and nand_isbad_bbt seems ok.
But just in case it could be changed to "if (!nand_block_isbad (nand, offset & (~nand->erasesize + 1))"

I do not understand what you mean about block_bad callbacks.

An individual NAND driver can provide its own block_bad() method in the nand_chip struct, and the semantics of the "ofs" parameter aren't described.

There's no code currently in-tree that would have a problem with it, but it might be better to mask it anyway.

-Scott