Discussion:
[patch] add support for ECC'd NOR chips to JFFS2
Josh Boyer
2004-10-28 18:53:46 UTC
Permalink
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 <***@us.ibm.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 ***@axis.com

Loading...