[patch] add support for ECC’d NOR chips to JFFS2
- To: jffs-dev@xxxxxxxx
- Subject: [patch] add support for ECC’d NOR chips to JFFS2
- From: Josh Boyer <jdub@xxxxxxxxxx>
- Date: Thu, 28 Oct 2004 13:53:46 -0500
- Sender: owner-jffs-dev@xxxxxxx
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 <[email protected]> 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: %dn", 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 errorn", @@ -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 [email protected]
转载请注明:在路上 » [patch] add support for ECC’d NOR chips to JFFS2