Commit 777456d6 authored by David Woodhouse's avatar David Woodhouse

JFFS2: Add support for bizarre NOR flash with ECC.

Signed-off-by: default avatarJosh Boyer <jdub@us.ibm.com>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent a567a70e
......@@ -1179,6 +1179,15 @@ config JFFS2_FS_NAND
Say 'N' unless you have NAND flash.
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 ST Microelectronics.
config JFFS2_COMPRESSION_OPTIONS
bool "Advanced compression options for JFFS2"
depends on JFFS2_FS
......
#
# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
#
# $Id: Makefile.common,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $
# $Id: Makefile.common,v 1.7 2004/11/03 12:57:38 jwboyer Exp $
#
obj-$(CONFIG_JFFS2_FS) += jffs2.o
......@@ -12,6 +12,7 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
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
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: erase.c,v 1.61 2004/10/20 23:59:49 dwmw2 Exp $
* $Id: erase.c,v 1.65 2004/11/13 10:51:47 dedekind Exp $
*
*/
......@@ -43,6 +43,7 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
jffs2_erase_succeeded(c, jeb);
return;
}
bad_offset = jeb->offset;
#else /* Linux */
struct erase_info *instr;
......@@ -386,6 +387,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
jeb->dirty_size = 0;
jeb->wasted_size = 0;
} else {
struct kvec vecs[1];
struct jffs2_unknown_node marker = {
.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
......@@ -394,8 +396,10 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
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);
vecs[0].iov_base = (unsigned char *) &marker;
vecs[0].iov_len = sizeof(marker);
ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
if (ret) {
printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
jeb->offset, ret);
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $
* $Id: fs.c,v 1.47 2004/11/03 12:57:39 jwboyer Exp $
*
*/
......@@ -649,6 +649,11 @@ int jffs2_flash_setup(struct jffs2_sb_info *c) {
}
/* 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 @@ void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
}
/* add cleanups for other bizarre flashes here... */
if (jffs2_nor_ecc(c)) {
jffs2_nor_ecc_flash_cleanup(c);
}
}
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $
* $Id: os-linux.h,v 1.50 2004/11/04 22:10:28 jwboyer Exp $
*
*/
......@@ -99,7 +99,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#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)
......@@ -115,10 +115,13 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
#define jffs2_wbuf_timeout NULL
#define jffs2_wbuf_process NULL
#define jffs2_nor_ecc(c) (0)
#define jffs2_nor_ecc_flash_setup(c) (0)
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
#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 +138,19 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc
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(c) (0)
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
#endif /* NOR ECC */
#endif /* NAND */
/* erase.c */
......
......@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: scan.c,v 1.112 2004/09/12 09:56:13 gleixner Exp $
* $Id: scan.c,v 1.113 2004/11/03 12:57:39 jwboyer Exp $
*
*/
#include <linux/kernel.h>
......@@ -68,7 +68,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
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 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
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,
......
......@@ -9,7 +9,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: wbuf.c,v 1.72 2004/09/11 19:22:43 gleixner Exp $
* $Id: wbuf.c,v 1.76 2004/11/05 12:41:10 jwboyer Exp $
*
*/
......@@ -224,7 +224,11 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
}
/* 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 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
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 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
*/
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 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
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 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
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 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
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 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
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 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
/* 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,24 @@ void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
{
kfree(c->wbuf);
}
#ifdef CONFIG_JFFS2_FS_NOR_ECC
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);
}
#endif
/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */
/* $Id: jffs2_fs_sb.h,v 1.46 2004/11/03 12:57:39 jwboyer Exp $ */
#ifndef _JFFS2_FS_SB
#define _JFFS2_FS_SB
......@@ -95,7 +95,7 @@ struct jffs2_sb_info {
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment