[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[patch] add support for ECC'd NOR chips to JFFS2



Hi all,

Below is a patch that adds support to JFFS2 for the ECC'd NOR flash
chips from STMicro.  These specific chips use the cfi_cmdset_0020.c
driver that's been in the MTD tree for a couple years now.

Basically, these chips are NOR flash that has transparent ECC.  Because
of the ECC, an erase needs to be done before a page can be written to
again, very similar to NAND flash.  In order to accomplish this, a wbuf
type mechanism is needed.

The patch adds a new config option, CONFIG_JFFS2_FS_NOR_ECC.  I'm not
too fond of this, but in the interest of not differing from the current
MTD code for now, that is what was done.  I'm always open for discussion
:).

Please consider accepting the patch for inclusion into CVS. 
Questions/comments are welcome.

Thanks,
josh

Signed-off-by: Josh Boyer <jdub@xxxxxxx.com>

diff -Naur -x CVS mtd/fs/jffs2/erase.c mtd.cvs/fs/jffs2/erase.c
--- mtd/fs/jffs2/erase.c	2004-10-21 13:15:09.000000000 -0500
+++ mtd.cvs/fs/jffs2/erase.c	2004-10-22 08:50:17.000000000 -0500
@@ -395,7 +395,15 @@
 		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
 
 		/* We only write the header; the rest was noise or padding anyway */
-		ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+		/* Q: why can't we use jffs2_flash_write?
+		   A: because we aren't holding alloc_sem here...  which is worse,
+		   grabbing alloc_sem to write out the marker, or just writing it...
+		   i think grabbing alloc_sem, so we just write it */
+		if (jffs2_nor_ecc(c))
+			ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+		else
+			ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+		
 		if (ret) {
 			printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
 			       jeb->offset, ret);
diff -Naur -x CVS mtd/fs/jffs2/fs.c mtd.cvs/fs/jffs2/fs.c
--- mtd/fs/jffs2/fs.c	2004-07-13 03:56:54.000000000 -0500
+++ mtd.cvs/fs/jffs2/fs.c	2004-08-16 08:41:48.000000000 -0500
@@ -649,6 +649,11 @@
 	}
 
 	/* add setups for other bizarre flashes here... */
+	if (jffs2_nor_ecc(c)) {
+		ret = jffs2_nor_ecc_flash_setup(c);
+		if (ret)
+			return ret;
+	}
 	return ret;
 }
 
@@ -659,4 +664,7 @@
 	}
 
 	/* add cleanups for other bizarre flashes here... */
+	if (jffs2_nor_ecc(c)) {
+		jffs2_nor_ecc_flash_cleanup(c);
+	}
 }
diff -Naur -x CVS mtd/fs/jffs2/Makefile.common mtd.cvs/fs/jffs2/Makefile.common
--- mtd/fs/jffs2/Makefile.common	2004-07-16 10:17:57.000000000 -0500
+++ mtd.cvs/fs/jffs2/Makefile.common	2004-08-16 08:41:48.000000000 -0500
@@ -12,6 +12,7 @@
 jffs2-y	+= super.o
 
 jffs2-$(CONFIG_JFFS2_FS_NAND)	+= wbuf.o
+jffs2-$(CONFIG_JFFS2_FS_NOR_ECC) += wbuf.o
 jffs2-$(CONFIG_JFFS2_RUBIN)	+= compr_rubin.o
 jffs2-$(CONFIG_JFFS2_RTIME)	+= compr_rtime.o
 jffs2-$(CONFIG_JFFS2_ZLIB)	+= compr_zlib.o
diff -Naur -x CVS mtd/fs/jffs2/os-linux.h mtd.cvs/fs/jffs2/os-linux.h
--- mtd/fs/jffs2/os-linux.h	2004-07-14 08:20:23.000000000 -0500
+++ mtd.cvs/fs/jffs2/os-linux.h	2004-08-16 08:41:48.000000000 -0500
@@ -99,7 +99,7 @@
 
 #define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
 
-#ifndef CONFIG_JFFS2_FS_NAND
+#if (!defined CONFIG_JFFS2_FS_NAND && !defined CONFIG_JFFS2_FS_NOR_ECC)
 #define jffs2_can_mark_obsolete(c) (1)
 #define jffs2_cleanmarker_oob(c) (0)
 #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
@@ -116,9 +116,9 @@
 #define jffs2_wbuf_timeout NULL
 #define jffs2_wbuf_process NULL
 
-#else /* NAND support present */
+#else /* NAND and/or ECC'd NOR support present */
 
-#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM)
+#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
 #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
 
 #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
@@ -135,8 +135,19 @@
 int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
 void jffs2_wbuf_timeout(unsigned long data);
 void jffs2_wbuf_process(void *data);
+int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
+int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
 int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
 void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
+#ifdef CONFIG_JFFS2_FS_NOR_ECC
+#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
+int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
+void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
+#else
+#define jffs2_nor_ecc(c) (0)
+#define jffs2_nor_ecc_flash_setup (0)
+#define jffs2_nor_ecc_flash_cleanup do {} while (0)
+#endif /* NOR ECC */
 #endif /* NAND */
 
 /* erase.c */
diff -Naur -x CVS mtd/fs/jffs2/scan.c mtd.cvs/fs/jffs2/scan.c
--- mtd/fs/jffs2/scan.c	2004-09-12 04:56:13.000000000 -0500
+++ mtd.cvs/fs/jffs2/scan.c	2004-09-13 14:13:57.000000000 -0500
@@ -68,7 +68,7 @@
 static inline int min_free(struct jffs2_sb_info *c)
 {
 	uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
-#ifdef CONFIG_JFFS2_FS_NAND
+#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
 	if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
 		return c->wbuf_pagesize;
 #endif
@@ -223,7 +223,7 @@
 		c->dirty_size -= c->nextblock->dirty_size;
 		c->nextblock->dirty_size = 0;
 	}
-#ifdef CONFIG_JFFS2_FS_NAND
+#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
 	if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
 		/* If we're going to start writing into a block which already 
 		   contains data, and the end of the data isn't page-aligned,
diff -Naur -x CVS mtd/fs/jffs2/wbuf.c mtd.cvs/fs/jffs2/wbuf.c
--- mtd/fs/jffs2/wbuf.c	2004-09-11 14:22:43.000000000 -0500
+++ mtd.cvs/fs/jffs2/wbuf.c	2004-09-13 14:16:40.000000000 -0500
@@ -224,7 +224,11 @@
 		}
 
 		/* Do the read... */
-		ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
+		if (jffs2_cleanmarker_oob(c))
+			ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
+		else
+			ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
+		
 		if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) {
 			/* ECC recovered */
 			ret = 0;
@@ -281,8 +285,11 @@
 			ret = -EIO;
 		} else
 #endif
+		if (jffs2_cleanmarker_oob(c))
 			ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
 						buf, NULL, c->oobinfo);
+		else
+			ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, buf);
 
 		if (ret || retlen != towrite) {
 			/* Argh. We tried. Really we did. */
@@ -419,6 +426,10 @@
 	*/
 	if (pad) {
 		c->wbuf_len = PAD(c->wbuf_len);
+
+		/* Pad with JFFS2_DIRTY_BITMASK initially.  this helps out ECC'd NOR
+		   with 8 byte page size */
+		memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
 		
 		if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
 			struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
@@ -426,9 +437,6 @@
 			padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
 			padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
 			padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
-		} else {
-			/* Pad with JFFS2_DIRTY_BITMASK */
-			memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
 		}
 	}
 	/* else jffs2_flash_writev has actually filled in the rest of the
@@ -444,8 +452,11 @@
 		ret = -EIO;
 	} else 
 #endif
-	ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
-
+	
+	if (jffs2_cleanmarker_oob(c))
+		ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
+	else
+		ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
 
 	if (ret || retlen != c->wbuf_pagesize) {
 		if (ret)
@@ -582,6 +593,17 @@
 		memset(c->wbuf,0xff,c->wbuf_pagesize);
 	}
 
+	/* Fixup the wbuf if we are moving to a new eraseblock.  The checks below
+	   fail for ECC'd NOR because cleanmarker == 16, so a block starts at
+	   xxx0010.  */
+	if (jffs2_nor_ecc(c)) {
+		if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) {
+			c->wbuf_ofs = PAGE_DIV(to);
+			c->wbuf_len = PAGE_MOD(to);
+			memset(c->wbuf,0xff,c->wbuf_pagesize);
+		}
+	}
+	
 	/* Sanity checks on target address. 
 	   It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), 
 	   and it's permitted to write at the beginning of a new 
@@ -715,7 +737,11 @@
 		outvecs[splitvec].iov_len = split_ofs;
 
 		/* We did cross a page boundary, so we write some now */
-		ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); 
+		if (jffs2_cleanmarker_oob(c))
+			ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); 
+		else
+			ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
+		
 		if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
 			/* At this point we have no problem,
 			   c->wbuf is empty. 
@@ -789,7 +815,10 @@
 
 	/* Read flash */
 	if (!jffs2_can_mark_obsolete(c)) {
-		ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
+		if (jffs2_cleanmarker_oob(c))
+			ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
+		else
+			ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
 
 		if ( (ret == -EBADMSG) && (*retlen == len) ) {
 			printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
@@ -1105,3 +1134,22 @@
 {
 	kfree(c->wbuf);
 }
+
+int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
+	/* Cleanmarker is actually larger on the flashes */
+	c->cleanmarker_size = 16;
+
+	/* Initialize write buffer */
+	c->wbuf_pagesize = c->mtd->eccsize;
+	c->wbuf_ofs = 0xFFFFFFFF;
+
+	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+	if (!c->wbuf)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
+	kfree(c->wbuf);
+}
diff -Naur -x CVS mtd/fs/Kconfig mtd.cvs/fs/Kconfig
--- mtd/fs/Kconfig	2004-07-16 10:20:59.000000000 -0500
+++ mtd.cvs/fs/Kconfig	2004-08-16 08:41:48.000000000 -0500
@@ -68,6 +68,15 @@
 	  Say 'N' unless you have NAND flash and you are willing to test and
 	  develop JFFS2 support for it.
 
+config JFFS2_FS_NOR_ECC
+	bool "JFFS2 support for ECC'd NOR flash (EXPERIMENTAL)"
+	   depends on JFFS2_FS && EXPERIMENTAL
+	   default n
+	   help
+	     This enables the experimental support for NOR flash with transparent
+		 ECC for JFFS2.  This type of flash chip is not common, however it is
+		 available from STMicro.
+
 config JFFS2_COMPRESSION_OPTIONS
 	bool "Advanced compression options for JFFS2"
 	default n
diff -Naur -x CVS mtd/include/linux/jffs2_fs_sb.h mtd.cvs/include/linux/jffs2_fs_sb.h
--- mtd/include/linux/jffs2_fs_sb.h	2003-10-08 06:46:27.000000000 -0500
+++ mtd.cvs/include/linux/jffs2_fs_sb.h	2004-08-16 08:41:48.000000000 -0500
@@ -95,7 +95,7 @@
 	   to an obsoleted node. I don't like this. Alternatives welcomed. */
 	struct semaphore erase_free_sem;
 
-#ifdef CONFIG_JFFS2_FS_NAND
+#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
 	/* Write-behind buffer for NAND flash */
 	unsigned char *wbuf;
 	uint32_t wbuf_ofs;


To unsubscribe from this list: send the line "unsubscribe jffs-dev" in
the body of a message to majordomo@xxxxxxx.com