[U-Boot] [PATCH][RFC] OneNAND: erase/read/write jffs2 commands

Fathi BOUDRA fabo at debian.org
Mon Aug 11 14:33:14 CEST 2008


Implement OneNAND related JFFS2 commands (based on NAND source code):
 - onenand erase clean
 - onenand read.jffs2
 - onenand write.jffs2

It contains also "Fill in remaining MTD driver data for OneNAND" sent last 
week.

This patch must be applied after "JFFS2 command support on OneNAND" patch sent 
also previously.

I can submit a cumulative patch if needed.

Signed-off-by: Fathi Boudra <fabo at debian.org>
---
--- a/common/cmd_onenand.c
+++ b/common/cmd_onenand.c
@@ -26,6 +26,11 @@
 int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 {
 	int ret = 0;
+#ifdef CFG_ONENAND_QUIET
+	int quiet = CFG_ONENAND_QUIET;
+#else
+	int quiet = 0;
+#endif

 	switch (argc) {
 	case 0:
@@ -38,7 +43,7 @@
 			onenand_init();
 			return 0;
 		}
-		onenand_print_device_info(onenand_chip.device_id, 1);
+		printf("%s\n", onenand_mtd.name);
 		return 0;

 	default:
@@ -51,7 +56,27 @@
 			ulong block;
 			char *endtail;

-			if (strncmp(argv[2], "block", 5) == 0) {
+			if (strncmp(argv[2], "clean", 5) == 0) {
+				ulong ofs = simple_strtoul(argv[3], NULL, 16);
+				size_t len = 0;
+
+				if (argc == 4) {
+					len = onenand_mtd.size - ofs;
+				} else {
+					len = simple_strtoul(argv[4], NULL, 16);
+				}
+
+				onenand_erase_options_t opts;
+
+				memset(&opts, 0, sizeof(opts));
+				opts.offset = ofs;
+				opts.length = len;
+				opts.jffs2  = 1;
+				opts.quiet  = quiet;
+				onenand_erase_opts(&onenand_mtd, &opts);
+
+				return 0;
+			} else 	if (strncmp(argv[2], "block", 5) == 0) {
 				start = simple_strtoul(argv[3], NULL, 10);
 				endtail = strchr(argv[3], '-');
 				end = simple_strtoul(endtail + 1, NULL, 10);
@@ -88,14 +113,25 @@
 			ulong ofs = simple_strtoul(argv[3], NULL, 16);
 			size_t len = simple_strtoul(argv[4], NULL, 16);
 			size_t retlen = 0;
-			int oob = strncmp(argv[1], "read.oob", 8) ? 0 : 1;

-			if (oob)
+			char *s = strchr(argv[1], '.');
+
+			if (s != NULL && !strncmp(s, ".jffs2", 6)) {
+				onenand_rw_options_t opts;
+
+				memset(&opts, 0, sizeof(opts));
+				opts.buffer	= (u_char*) addr;
+				opts.length	= len;
+				opts.offset	= ofs;
+				opts.quiet      = quiet;
+				onenand_read_opts(&onenand_mtd, &opts);
+			} else if (s != NULL && !strncmp(s, ".oob", 4)) {
 				onenand_read_oob(&onenand_mtd, ofs, len,
 						 &retlen, (u_char *) addr);
-			else
+			} else {
 				onenand_read(&onenand_mtd, ofs, len, &retlen,
 					     (u_char *) addr);
+			}
 			printf("Done\n");

 			return 0;
@@ -107,8 +143,23 @@
 			size_t len = simple_strtoul(argv[4], NULL, 16);
 			size_t retlen = 0;

-			onenand_write(&onenand_mtd, ofs, len, &retlen,
-				      (u_char *) addr);
+			char *s = strchr(argv[1], '.');
+
+			if (s != NULL && !strncmp(s, ".jffs2", 6)) {
+				onenand_rw_options_t opts;
+
+				memset(&opts, 0, sizeof(opts));
+				opts.buffer	= (u_char*) addr;
+				opts.length	= len;
+				opts.offset	= ofs;
+				opts.pad	= 1;
+				opts.blockalign = 1;
+				opts.quiet	= quiet;
+				onenand_write_opts(&onenand_mtd, &opts);
+			} else {
+				onenand_write(&onenand_mtd, ofs, len, &retlen,
+					(u_char *) addr);
+			}
 			printf("Done\n");

 			return 0;
@@ -153,9 +204,9 @@
 	onenand,	6,	1,	do_onenand,
 	"onenand - OneNAND sub-system\n",
 	"info   - show available OneNAND devices\n"
-	"onenand read[.oob] addr ofs len - read data at ofs with len to addr\n"
-	"onenand write addr ofs len - write data at ofs with len from addr\n"
-	"onenand erase saddr eaddr - erase block start addr to end addr\n"
+	"onenand read[.jffs2][.oob] addr ofs len - read data at ofs with len to 
addr\n"
+	"onenand write[.jffs2] addr ofs len - write data at ofs with len from addr\n"
+	"onenand erase [clean] saddr eaddr - erase block start addr to end addr\n"
 	"onenand block[.oob] addr block [page] [len] - "
 		"read data with (block [, page]) to addr"
 );
--- a/include/onenand_uboot.h
+++ b/include/onenand_uboot.h
@@ -29,6 +29,35 @@

 extern struct mtd_info onenand_mtd;

+struct onenand_rw_options {
+	u_char *buffer;	/* memory block containing image to read/write */
+	ulong length;	/* number of bytes to read/write */
+	ulong offset;	/* start address in OneNAND */
+	int quiet;	/* don't display progress messages */
+	int readoob;	/* put oob data in image */
+	int autoplace;	/* if true use auto oob layout */
+	int forcejffs2;	/* force jffs2 oob layout */
+	int forceyaffs;	/* force yaffs oob layout */
+	int noecc;	/* write without ecc */
+	int writeoob;	/* image contains oob data */
+	int pad;	/* pad to page size */
+	int blockalign;	/* 1|2|4 set multiple of eraseblocks to align to */
+};
+
+typedef struct onenand_rw_options onenand_rw_options_t;
+
+struct onenand_erase_options {
+	ulong length;	/* number of bytes to erase */
+	ulong offset;	/* first address in NAND to erase */
+	int quiet;	/* don't display progress messages */
+	int jffs2;	/* if true: format for jffs2 usage
+			 * (write appropriate cleanmarker blocks) */
+	int scrub;	/* if true, really clean NAND by erasing
+			 * bad blocks (UNSAFE) */
+};
+
+typedef struct onenand_erase_options onenand_erase_options_t;
+
 /* Functions */
 extern void onenand_init(void);
 extern int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -41,6 +70,19 @@

 extern int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);

-extern void onenand_print_device_info(int device, int verbose);
+extern char * onenand_print_device_info(int device);
+
+extern int onenand_write_opts(struct mtd_info *meminfo,
+			const onenand_rw_options_t *opts);
+
+extern int onenand_read_opts(struct mtd_info *meminfo,
+			const onenand_rw_options_t *opts);
+
+extern int onenand_erase_opts(struct mtd_info *meminfo,
+			const onenand_erase_options_t *opts);
+
+extern int onenand_default_bbt(struct mtd_info *mtd);
+
+extern int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);

 #endif /* __UBOOT_ONENAND_H */
--- a/drivers/mtd/onenand/onenand_uboot.c
+++ b/drivers/mtd/onenand/onenand_uboot.c
@@ -17,13 +17,23 @@

 #ifdef CONFIG_CMD_ONENAND

+#include <watchdog.h>
+#include <malloc.h>
+#include <div64.h>
+
 #include <linux/mtd/compat.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/onenand.h>
+#include <jffs2/jffs2.h>

+typedef struct erase_info erase_info_t;
 struct mtd_info onenand_mtd;
 struct onenand_chip onenand_chip;

+/* support only for native endian JFFS2 */
+#define cpu_to_je16(x) (x)
+#define cpu_to_je32(x) (x)
+
 void onenand_init(void)
 {
 	memset(&onenand_mtd, 0, sizeof(struct mtd_info));
@@ -38,4 +48,511 @@
 	print_size(onenand_mtd.size, "\n");
 }

+/**
+ * onenand_erase_opts: - erase OneNAND flash with support for various options
+ *			(jffs2 formating)
+ *
+ * @param meminfo	OneNAND device to erase
+ * @param opts		options,  @see struct onenand_erase_options
+ * @return		0 in case of success
+ *
+ * This code is ported from flash_eraseall.c from Linux mtd utils by
+ * Arcom Control System Ltd.
+ */
+int onenand_erase_opts(struct mtd_info *meminfo,
+		const onenand_erase_options_t *opts)
+{
+	struct jffs2_unknown_node cleanmarker;
+	int clmpos = 0;
+	int clmlen = 8;
+	erase_info_t erase;
+	ulong erase_length;
+	int bbtest = 1;
+	int result;
+	int percent_complete = -1;
+	const char *mtd_device = meminfo->name;
+
+	memset(&erase, 0, sizeof(erase));
+
+	erase.mtd = meminfo;
+	erase.len  = meminfo->erasesize;
+	erase.addr = opts->offset;
+	erase_length = opts->length;
+
+	if (opts->jffs2) {
+		cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
+		cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
+		clmpos = 8;
+		clmlen = 8;
+		cleanmarker.totlen = cpu_to_je32(8);
+		cleanmarker.hdr_crc =  cpu_to_je32(
+			crc32_no_comp(0, (unsigned char *) &cleanmarker,
+				      sizeof(struct jffs2_unknown_node) - 4));
+	}
+
+	for (;
+	     erase.addr < opts->offset + erase_length;
+	     erase.addr += meminfo->erasesize) {
+
+		WATCHDOG_RESET ();
+
+		if (!opts->scrub && bbtest) {
+			int ret = meminfo->block_isbad(meminfo, erase.addr);
+			if (ret > 0) {
+				if (!opts->quiet)
+					printf("\rSkipping bad block at  "
+					       "0x%08x                   "
+					       "                         \n",
+					       erase.addr);
+				continue;
+
+			} else if (ret < 0) {
+				printf("\n%s: MTD get bad block failed: %d\n",
+				       mtd_device,
+				       ret);
+				return -1;
+			}
+		}
+
+		result = meminfo->erase(meminfo, &erase);
+		if (result != 0) {
+			printf("\n%s: MTD Erase failure: %d\n",
+			       mtd_device, result);
+			continue;
+		}
+
+		/* format for JFFS2 ? */
+		if (opts->jffs2) {
+
+			/* write cleanmarker */
+			size_t written;
+			result = meminfo->write_oob(meminfo,
+						    erase.addr + clmpos,
+						    clmlen,
+						    &written,
+						    (unsigned char *)
+						    &cleanmarker);
+			if (result != 0) {
+				printf("\n%s: MTD writeoob failure: %d\n",
+				       mtd_device, result);
+				continue;
+			}
+		}
+
+		if (!opts->quiet) {
+			unsigned long long n =(unsigned long long)
+				(erase.addr + meminfo->erasesize - opts->offset)
+				* 100;
+			int percent;
+
+			do_div(n, erase_length);
+			percent = (int)n;
+
+			/* output progress message only at whole percent
+			 * steps to reduce the number of messages printed
+			 * on (slow) serial consoles
+			 */
+			if (percent != percent_complete) {
+				percent_complete = percent;
+
+				printf("\rErasing at 0x%x -- %3d%% complete.",
+				       erase.addr, percent);
+
+				if (opts->jffs2 && result == 0)
+					printf(" Cleanmarker written at 0x%x.",
+					       erase.addr);
+			}
+		}
+	}
+	if (!opts->quiet)
+		printf("\n");
+
+	return 0;
+}
+
+#define MAX_PAGE_SIZE	2048
+#define MAX_OOB_SIZE	64
+
+/*
+ * buffer array used for writing data
+ */
+static unsigned char data_buf[MAX_PAGE_SIZE];
+static unsigned char oob_buf[MAX_OOB_SIZE];
+
+/**
+ * onenand_write_opts: - write image to OneNAND flash with support for various
+ *			options
+ *
+ * @param meminfo	OneNAND device to write
+ * @param opts		write options (@see onenand_write_options)
+ * @return		0 in case of success
+ *
+ * This code is ported from nandwrite.c from Linux mtd utils by
+ * Steven J. Hill and Thomas Gleixner.
+ */
+int onenand_write_opts(struct mtd_info *meminfo,
+		const onenand_rw_options_t *opts)
+{
+	int imglen = 0;
+	int pagelen;
+	int baderaseblock;
+	int blockstart = -1;
+	loff_t offs;
+	int readlen;
+	int percent_complete = -1;
+	ulong mtdoffset = opts->offset;
+	ulong erasesize_blockalign;
+	u_char *buffer = opts->buffer;
+	size_t written;
+	int result;
+
+	if (opts->pad && opts->writeoob) {
+		printf("Can't pad when oob data is present.\n");
+		return -1;
+	}
+
+	/* set erasesize to specified number of blocks - to match
+	 * jffs2 (virtual) block size */
+	if (opts->blockalign == 0)
+		erasesize_blockalign = meminfo->erasesize;
+	else
+		erasesize_blockalign = meminfo->erasesize * opts->blockalign;
+
+	/* make sure device page sizes are valid */
+	if (!(meminfo->oobsize == 32 && meminfo->oobblock == 1024)
+	    && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
+		printf("Unknown flash (not normal OneNAND)\n");
+		return -1;
+	}
+
+	/* get image length */
+	imglen = opts->length;
+	pagelen = meminfo->oobblock
+		+ ((opts->writeoob != 0) ? meminfo->oobsize : 0);
+
+	/* check, if file is pagealigned */
+	if ((!opts->pad) && ((imglen % pagelen) != 0)) {
+		printf("Input block length is not page aligned\n");
+		goto restoreoob;
+	}
+
+	/* check, if length fits into device */
+	if (((imglen / pagelen) * meminfo->oobblock)
+	     > (meminfo->size - opts->offset)) {
+		printf("Image %d bytes, OneNAND page %d bytes, "
+		       "OOB area %u bytes, device size %u bytes\n",
+		       imglen, pagelen, meminfo->oobblock, meminfo->size);
+		printf("Input block does not fit into device\n");
+		goto restoreoob;
+	}
+
+	if (!opts->quiet)
+		printf("\n");
+
+	/* get data from input and write to the device */
+	while (imglen && (mtdoffset < meminfo->size)) {
+
+		WATCHDOG_RESET ();
+
+		/*
+		 * new eraseblock, check for bad block(s). Stay in the
+		 * loop to be sure if the offset changes because of
+		 * a bad block, that the next block that will be
+		 * written to is also checked. Thus avoiding errors if
+		 * the block(s) after the skipped block(s) is also bad
+		 * (number of blocks depending on the blockalign
+		 */
+		while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
+			blockstart = mtdoffset & (~erasesize_blockalign+1);
+			offs = blockstart;
+			baderaseblock = 0;
+
+			/* check all the blocks in an erase block for
+			 * bad blocks */
+			do {
+				int ret = meminfo->block_isbad(meminfo, offs);
+
+				if (ret < 0) {
+					printf("Bad block check failed\n");
+					goto restoreoob;
+				}
+				if (ret == 1) {
+					baderaseblock = 1;
+					if (!opts->quiet)
+						printf("\rBad block at 0x%lx "
+						       "in erase block from "
+						       "0x%x will be skipped\n",
+						       (long) offs,
+						       blockstart);
+				}
+
+				if (baderaseblock) {
+					mtdoffset = blockstart
+						+ erasesize_blockalign;
+				}
+				offs +=	 erasesize_blockalign
+					/ opts->blockalign;
+			} while (offs < blockstart + erasesize_blockalign);
+		}
+
+		readlen = meminfo->oobblock;
+		if (opts->pad && (imglen < readlen)) {
+			readlen = imglen;
+			memset(data_buf + readlen, 0xff,
+			       meminfo->oobblock - readlen);
+		}
+
+		/* read page data from input memory buffer */
+		memcpy(data_buf, buffer, readlen);
+		buffer += readlen;
+
+		if (opts->writeoob) {
+			/* read OOB data from input memory block, exit
+			 * on failure */
+			memcpy(oob_buf, buffer, meminfo->oobsize);
+			buffer += meminfo->oobsize;
+
+			/* write OOB data first, as ecc will be placed
+			 * in there*/
+			result = meminfo->write_oob(meminfo,
+						    mtdoffset,
+						    meminfo->oobsize,
+						    &written,
+						    (unsigned char *)
+						    &oob_buf);
+
+			if (result != 0) {
+				printf("\nMTD writeoob failure: %d\n",
+				       result);
+				goto restoreoob;
+			}
+			imglen -= meminfo->oobsize;
+		}
+
+		/* write out the page data */
+		result = meminfo->write(meminfo,
+					mtdoffset,
+					meminfo->oobblock,
+					&written,
+					(unsigned char *) &data_buf);
+
+		if (result != 0) {
+			printf("writing OneNAND page at offset 0x%lx failed\n",
+			       mtdoffset);
+			goto restoreoob;
+		}
+
+		imglen -= readlen;
+
+		if (!opts->quiet) {
+			unsigned long long n = (unsigned long long)
+				 (opts->length-imglen) * 100;
+			int percent;
+
+			do_div(n, opts->length);
+			percent = (int)n;
+
+			/* output progress message only at whole percent
+			 * steps to reduce the number of messages printed
+			 * on (slow) serial consoles
+			 */
+			if (percent != percent_complete) {
+				printf("\rWriting data at 0x%x "
+				       "-- %3d%% complete.",
+				       mtdoffset, percent);
+				percent_complete = percent;
+			}
+		}
+
+		mtdoffset += meminfo->oobblock;
+	}
+
+	if (!opts->quiet)
+		printf("\n");
+
+restoreoob:
+	if (imglen > 0) {
+		printf("Data did not fit into device, due to bad blocks\n");
+		return -1;
+	}
+
+	/* return happy */
+	return 0;
+}
+
+/**
+ * onenand_read_opts: - read image from OneNAND flash with support for various
+ *			options
+ *
+ * @param meminfo	OneNAND device to erase
+ * @param opts		read options (@see struct onenand_read_options)
+ * @return		0 in case of success
+ *
+ */
+int onenand_read_opts(struct mtd_info *meminfo,
+		const onenand_rw_options_t *opts)
+{
+	int imglen = opts->length;
+	int pagelen;
+	int baderaseblock;
+	int blockstart = -1;
+	int percent_complete = -1;
+	loff_t offs;
+	size_t readlen;
+	ulong mtdoffset = opts->offset;
+	u_char *buffer = opts->buffer;
+	int result;
+
+	/* make sure device page sizes are valid */
+	if (!(meminfo->oobsize == 32 && meminfo->oobblock == 1024)
+	    && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
+		printf("Unknown flash (not normal OneNAND)\n");
+		return -1;
+	}
+
+	pagelen = meminfo->oobblock
+		+ ((opts->readoob != 0) ? meminfo->oobsize : 0);
+
+	/* check, if length is not larger than device */
+	if (((imglen / pagelen) * meminfo->oobblock)
+	     > (meminfo->size - opts->offset)) {
+		printf("Image %d bytes, OneNAND page %d bytes, "
+		       "OOB area %u bytes, device size %u bytes\n",
+		       imglen, pagelen, meminfo->oobblock, meminfo->size);
+		printf("Input block is larger than device\n");
+		return -1;
+	}
+
+	if (!opts->quiet)
+		printf("\n");
+
+	/* get data from input and write to the device */
+	while (imglen && (mtdoffset < meminfo->size)) {
+
+		WATCHDOG_RESET ();
+
+		/*
+		 * new eraseblock, check for bad block(s). Stay in the
+		 * loop to be sure if the offset changes because of
+		 * a bad block, that the next block that will be
+		 * written to is also checked. Thus avoiding errors if
+		 * the block(s) after the skipped block(s) is also bad
+		 * (number of blocks depending on the blockalign
+		 */
+		while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) {
+			blockstart = mtdoffset & (~meminfo->erasesize+1);
+			offs = blockstart;
+			baderaseblock = 0;
+
+			/* check all the blocks in an erase block for
+			 * bad blocks */
+			do {
+				int ret = meminfo->block_isbad(meminfo, offs);
+
+				if (ret < 0) {
+					printf("Bad block check failed\n");
+					return -1;
+				}
+				if (ret == 1) {
+					baderaseblock = 1;
+					if (!opts->quiet)
+						printf("\rBad block at 0x%lx "
+						       "in erase block from "
+						       "0x%x will be skipped\n",
+						       (long) offs,
+						       blockstart);
+				}
+
+				if (baderaseblock) {
+					mtdoffset = blockstart
+						+ meminfo->erasesize;
+				}
+				offs +=	 meminfo->erasesize;
+
+			} while (offs < blockstart + meminfo->erasesize);
+		}
+
+
+		/* read page data to memory buffer */
+		result = meminfo->read(meminfo,
+				       mtdoffset,
+				       meminfo->oobblock,
+				       &readlen,
+				       (unsigned char *) &data_buf);
+
+		if (result != 0) {
+			printf("reading OneNAND page at offset 0x%lx failed\n",
+			       mtdoffset);
+			return -1;
+		}
+
+		if (imglen < readlen) {
+			readlen = imglen;
+		}
+
+		memcpy(buffer, data_buf, readlen);
+		buffer += readlen;
+		imglen -= readlen;
+
+		if (opts->readoob) {
+			result = meminfo->read_oob(meminfo,
+						   mtdoffset,
+						   meminfo->oobsize,
+						   &readlen,
+						   (unsigned char *)
+						   &oob_buf);
+
+			if (result != 0) {
+				printf("\nMTD readoob failure: %d\n",
+				       result);
+				return -1;
+			}
+
+
+			if (imglen < readlen) {
+				readlen = imglen;
+			}
+
+			memcpy(buffer, oob_buf, readlen);
+
+			buffer += readlen;
+			imglen -= readlen;
+		}
+
+		if (!opts->quiet) {
+			unsigned long long n = (unsigned long long)
+				 (opts->length-imglen) * 100;
+			int percent;
+
+			do_div(n, opts->length);
+			percent = (int)n;
+
+			/* output progress message only at whole percent
+			 * steps to reduce the number of messages printed
+			 * on (slow) serial consoles
+			 */
+			if (percent != percent_complete) {
+			if (!opts->quiet)
+				printf("\rReading data from 0x%x "
+				       "-- %3d%% complete.",
+				       mtdoffset, percent);
+				percent_complete = percent;
+			}
+		}
+
+		mtdoffset += meminfo->oobblock;
+	}
+
+	if (!opts->quiet)
+		printf("\n");
+
+	if (imglen > 0) {
+		printf("Could not read entire image due to bad blocks\n");
+		return -1;
+	}
+
+	/* return happy */
+	return 0;
+}
+
 #endif	/* CONFIG_CMD_ONENAND */
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -19,6 +19,7 @@

 #include <asm/io.h>
 #include <asm/errno.h>
+#include <malloc.h>

 /* It should access 16-bit instead of 8-bit */
 static inline void *memcpy_16(void *dst, const void *src, unsigned int len)
@@ -894,6 +895,23 @@
 }

 /**
+ * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @param mtd		MTD device structure
+ * @param ofs		offset from device start
+ * @param getchip	0, if the chip is already selected
+ * @param allowbbt	1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+			int allowbbt)
+{
+	/* Return info from the table */
+	return onenand_isbad_bbt(mtd, ofs, allowbbt);
+}
+
+/**
  * onenand_erase - [MTD Interface] erase block(s)
  * @param mtd		MTD device structure
  * @param instr		erase instruction
@@ -947,7 +965,14 @@

 	while (len) {

-		/* TODO Check badblock */
+		/* Check if we have a bad block, we do not erase bad blocks */
+		if (onenand_block_checkbad(mtd, addr, 0, 0)) {
+			printk (KERN_WARNING "onenand_erase: "
+			"attempt to erase a bad block at addr 0x%08x\n",
+			(unsigned int) addr);
+			instr->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}

 		this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);

@@ -1006,30 +1031,39 @@
  * onenand_block_isbad - [MTD Interface] Check whether the block at the given 
offset is bad
  * @param mtd		MTD device structure
  * @param ofs		offset relative to mtd start
+ *
+ * Check whether the block is bad
  */
 int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
 {
-	/*
-	 * TODO
-	 * 1. Bad block table (BBT)
-	 *   -> using NAND BBT to support JFFS2
-	 * 2. Bad block management (BBM)
-	 *   -> bad block replace scheme
-	 *
-	 * Currently we do nothing
-	 */
-	return 0;
+	/* Check for invalid offset */
+	if (ofs > mtd->size)
+		return -EINVAL;
+
+	return onenand_block_checkbad(mtd, ofs, 1, 0);
 }

 /**
  * onenand_block_markbad - [MTD Interface] Mark the block at the given offset 
as bad
  * @param mtd		MTD device structure
  * @param ofs		offset relative to mtd start
+ *
+ * Mark the block as bad
  */
 int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
-	/* see above */
-	return 0;
+	struct onenand_chip *this = mtd->priv;
+	int ret;
+
+	ret = onenand_block_isbad(mtd, ofs);
+	if (ret) {
+		/* If it was bad already, return success and do nothing */
+		if (ret > 0)
+			return 0;
+		return ret;
+	}
+
+	return this->block_markbad(mtd, ofs);
 }

 /**
@@ -1112,21 +1146,21 @@
  *
  * Print device ID
  */
-void onenand_print_device_info(int device, int verbose)
+char * onenand_print_device_info(int device)
 {
 	int vcc, demuxed, ddp, density;
-
-	if (!verbose)
-		return;
+	char *dev_info = malloc(80);

 	vcc = device & ONENAND_DEVICE_VCC_MASK;
 	demuxed = device & ONENAND_DEVICE_IS_DEMUX;
 	ddp = device & ONENAND_DEVICE_IS_DDP;
 	density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
-	printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
+	sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
 	       demuxed ? "" : "Muxed ",
 	       ddp ? "(DDP)" : "",
 	       (16 << density), vcc ? "2.65/3.3" : "1.8", device);
+
+	return dev_info;
 }

 static const struct onenand_manufacturers onenand_manuf_ids[] = {
@@ -1205,7 +1239,7 @@
 	}

 	/* Flash device information */
-	onenand_print_device_info(dev_id, 0);
+	mtd->name = onenand_print_device_info(dev_id);
 	this->device_id = dev_id;

 	density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
@@ -1241,6 +1275,20 @@
 		this->options |= ONENAND_CONT_LOCK;
 	}

+	/* Fill in remaining MTD driver data */
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->erase = onenand_erase;
+	mtd->read = onenand_read;
+	mtd->write = onenand_write;
+	mtd->read_ecc = onenand_read_ecc;
+	mtd->write_ecc = onenand_write_ecc;
+	mtd->read_oob = onenand_read_oob;
+	mtd->write_oob = onenand_write_oob;
+	mtd->sync = onenand_sync;
+	mtd->block_isbad = onenand_block_isbad;
+	mtd->block_markbad = onenand_block_markbad;
+
 	return 0;
 }

--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -103,6 +103,7 @@
 	unsigned short (*read_word) (void __iomem * addr);
 	void (*write_word) (unsigned short value, void __iomem * addr);
 	void (*mmcontrol) (struct mtd_info * mtd, int sync_read);
+	int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

 	spinlock_t chip_lock;
 	wait_queue_head_t wq;
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -146,7 +146,7 @@
  * @param offs		offset in the device
  * @param allowbbt	allow access to bad block table region
  */
-static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
 {
 	struct onenand_chip *this = mtd->priv;
 	struct bbm_info *bbm = this->bbm;




More information about the U-Boot mailing list