From linux-mtd at lists.infradead.org  Wed May  2 03:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 02 May 2007 08:59:01 +0100
Subject: [MTD] Add Kyungmin Park to MAINTAINERS for OneNAND
Message-ID: <E1Hj9jp-0005F5-Ji@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=431bca73f7bc96cf9d51ae9bfd783d3c5bafd7cb
Commit:     431bca73f7bc96cf9d51ae9bfd783d3c5bafd7cb
Parent:     f00546363fff1576ceddc2690d47e5f9c1dd2e05
Author:     Thomas Gleixner <tglx at linutronix.de>
AuthorDate: Wed May 2 09:31:35 2007 +0200
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 2 08:33:31 2007 +0100

    [MTD] Add Kyungmin Park to MAINTAINERS for OneNAND
    
    Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 MAINTAINERS |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 77bff8c..036e31f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2550,6 +2550,12 @@ M:	corbet at lwn.net
 L:	video4linux-list at redhat.com
 S:	Maintained
 
+ONENAND FLASH DRIVER
+P:	Kyungmin Park
+M:	kyungmin.park at samsung.com
+L:	linux-mtd at lists.infradead.org
+S:	Maintained
+
 ONSTREAM SCSI TAPE DRIVER
 P:	Willem Riede
 M:	osst at riede.org


From linux-mtd at lists.infradead.org  Wed May  2 03:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 02 May 2007 08:59:01 +0100
Subject: [JFFS2] Remove dead file histo_mips.h
Message-ID: <E1Hj9jp-0005FQ-Ro@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=7c96b7a146eb81d13be738709c36752c20abe4fc
Commit:     7c96b7a146eb81d13be738709c36752c20abe4fc
Parent:     431bca73f7bc96cf9d51ae9bfd783d3c5bafd7cb
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Wed May 2 08:36:21 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 2 08:36:21 2007 +0100

    [JFFS2] Remove dead file histo_mips.h
    
    Its contents were subsumed into compr_rubin.c in a previous
    commit, but I forgot to git-rm it.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 fs/jffs2/histo_mips.h |    2 --
 1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/fs/jffs2/histo_mips.h b/fs/jffs2/histo_mips.h
deleted file mode 100644
index fa3dac1..0000000
--- a/fs/jffs2/histo_mips.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#define BIT_DIVIDER_MIPS 1043
-static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */


From linux-mtd at lists.infradead.org  Wed May  2 07:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 02 May 2007 12:59:01 +0100
Subject: [RSLIB] Support non-canonical GF representations
Message-ID: <E1HjDU5-0005yy-Fz@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=d7e5a5462f68270ed66efff22b1981be57a28c19
Commit:     d7e5a5462f68270ed66efff22b1981be57a28c19
Parent:     7c96b7a146eb81d13be738709c36752c20abe4fc
Author:     Segher Boessenkool <segher at kernel.crashing.org>
AuthorDate: Wed May 2 12:18:41 2007 +0200
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 2 11:56:33 2007 +0100

    [RSLIB] Support non-canonical GF representations
    
    For the CAF? NAND controller, we need to support non-canonical
    representations of the Galois field. Allow the caller to provide its own
    function for generating the field, and CAF? can use rslib instead of its
    own implementation.
    
    Signed-off-by: Segher Boessenkool <segher at kernel.crashing.org>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 include/linux/rslib.h           |    4 ++
 lib/reed_solomon/reed_solomon.c |   84 ++++++++++++++++++++++++++++++++-------
 2 files changed, 73 insertions(+), 15 deletions(-)

diff --git a/include/linux/rslib.h b/include/linux/rslib.h
index ace25ac..746580c 100644
--- a/include/linux/rslib.h
+++ b/include/linux/rslib.h
@@ -34,6 +34,7 @@
  * @prim:	Primitive element, index form
  * @iprim:	prim-th root of 1, index form
  * @gfpoly:	The primitive generator polynominal
+ * @gffunc:	Function to generate the field, if non-canonical representation
  * @users:	Users of this structure
  * @list:	List entry for the rs control list
 */
@@ -48,6 +49,7 @@ struct rs_control {
 	int 		prim;
 	int 		iprim;
 	int		gfpoly;
+	int		(*gffunc)(int);
 	int		users;
 	struct list_head list;
 };
@@ -77,6 +79,8 @@ int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
 /* Create or get a matching rs control structure */
 struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
 			   int nroots);
+struct rs_control *init_rs_non_canonical(int symsize, int (*func)(int),
+                                         int fcr, int prim, int nroots);
 
 /* Release a rs control structure */
 void free_rs(struct rs_control *rs);
diff --git a/lib/reed_solomon/reed_solomon.c b/lib/reed_solomon/reed_solomon.c
index a4b730a..5b0d852 100644
--- a/lib/reed_solomon/reed_solomon.c
+++ b/lib/reed_solomon/reed_solomon.c
@@ -56,6 +56,7 @@ static DEFINE_MUTEX(rslistlock);
  * rs_init - Initialize a Reed-Solomon codec
  * @symsize:	symbol size, bits (1-8)
  * @gfpoly:	Field generator polynomial coefficients
+ * @gffunc:	Field generator function
  * @fcr:	first root of RS code generator polynomial, index form
  * @prim:	primitive element to generate polynomial roots
  * @nroots:	RS code generator polynomial degree (number of roots)
@@ -63,8 +64,8 @@ static DEFINE_MUTEX(rslistlock);
  * Allocate a control structure and the polynom arrays for faster
  * en/decoding. Fill the arrays according to the given parameters.
  */
-static struct rs_control *rs_init(int symsize, int gfpoly, int fcr,
-				   int prim, int nroots)
+static struct rs_control *rs_init(int symsize, int gfpoly, int (*gffunc)(int),
+                                  int fcr, int prim, int nroots)
 {
 	struct rs_control *rs;
 	int i, j, sr, root, iprim;
@@ -82,6 +83,7 @@ static struct rs_control *rs_init(int symsize, int gfpoly, int fcr,
 	rs->prim = prim;
 	rs->nroots = nroots;
 	rs->gfpoly = gfpoly;
+	rs->gffunc = gffunc;
 
 	/* Allocate the arrays */
 	rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
@@ -99,17 +101,26 @@ static struct rs_control *rs_init(int symsize, int gfpoly, int fcr,
 	/* Generate Galois field lookup tables */
 	rs->index_of[0] = rs->nn;	/* log(zero) = -inf */
 	rs->alpha_to[rs->nn] = 0;	/* alpha**-inf = 0 */
-	sr = 1;
-	for (i = 0; i < rs->nn; i++) {
-		rs->index_of[sr] = i;
-		rs->alpha_to[i] = sr;
-		sr <<= 1;
-		if (sr & (1 << symsize))
-			sr ^= gfpoly;
-		sr &= rs->nn;
+	if (gfpoly) {
+		sr = 1;
+		for (i = 0; i < rs->nn; i++) {
+			rs->index_of[sr] = i;
+			rs->alpha_to[i] = sr;
+			sr <<= 1;
+			if (sr & (1 << symsize))
+				sr ^= gfpoly;
+			sr &= rs->nn;
+		}
+	} else {
+		sr = gffunc(0);
+		for (i = 0; i < rs->nn; i++) {
+			rs->index_of[sr] = i;
+			rs->alpha_to[i] = sr;
+			sr = gffunc(sr);
+		}
 	}
 	/* If it's not primitive, exit */
-	if(sr != 1)
+	if(sr != rs->alpha_to[0])
 		goto errpol;
 
 	/* Find prim-th root of 1, used in decoding */
@@ -173,18 +184,22 @@ void free_rs(struct rs_control *rs)
 }
 
 /**
- * init_rs - Find a matching or allocate a new rs control structure
+ * init_rs_internal - Find a matching or allocate a new rs control structure
  *  @symsize:	the symbol size (number of bits)
  *  @gfpoly:	the extended Galois field generator polynomial coefficients,
  *		with the 0th coefficient in the low order bit. The polynomial
  *		must be primitive;
+ *  @gffunc:	pointer to function to generate the next field element,
+ *		or the multiplicative identity element if given 0.  Used
+ *		instead of gfpoly if gfpoly is 0
  *  @fcr:  	the first consecutive root of the rs code generator polynomial
  *		in index form
  *  @prim:	primitive element to generate polynomial roots
  *  @nroots:	RS code generator polynomial degree (number of roots)
  */
-struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
-			   int nroots)
+static struct rs_control *init_rs_internal(int symsize, int gfpoly,
+                                           int (*gffunc)(int), int fcr,
+                                           int prim, int nroots)
 {
 	struct list_head	*tmp;
 	struct rs_control	*rs;
@@ -208,6 +223,8 @@ struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
 			continue;
 		if (gfpoly != rs->gfpoly)
 			continue;
+		if (gffunc != rs->gffunc)
+			continue;
 		if (fcr != rs->fcr)
 			continue;
 		if (prim != rs->prim)
@@ -220,7 +237,7 @@ struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
 	}
 
 	/* Create a new one */
-	rs = rs_init(symsize, gfpoly, fcr, prim, nroots);
+	rs = rs_init(symsize, gfpoly, gffunc, fcr, prim, nroots);
 	if (rs) {
 		rs->users = 1;
 		list_add(&rs->list, &rslist);
@@ -230,6 +247,42 @@ out:
 	return rs;
 }
 
+/**
+ * init_rs - Find a matching or allocate a new rs control structure
+ *  @symsize:	the symbol size (number of bits)
+ *  @gfpoly:	the extended Galois field generator polynomial coefficients,
+ *		with the 0th coefficient in the low order bit. The polynomial
+ *		must be primitive;
+ *  @fcr:  	the first consecutive root of the rs code generator polynomial
+ *		in index form
+ *  @prim:	primitive element to generate polynomial roots
+ *  @nroots:	RS code generator polynomial degree (number of roots)
+ */
+struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
+                           int nroots)
+{
+	return init_rs_internal(symsize, gfpoly, NULL, fcr, prim, nroots);
+}
+
+/**
+ * init_rs_non_canonical - Find a matching or allocate a new rs control
+ *                         structure, for fields with non-canonical
+ *                         representation
+ *  @symsize:	the symbol size (number of bits)
+ *  @gffunc:	pointer to function to generate the next field element,
+ *		or the multiplicative identity element if given 0.  Used
+ *		instead of gfpoly if gfpoly is 0
+ *  @fcr:  	the first consecutive root of the rs code generator polynomial
+ *		in index form
+ *  @prim:	primitive element to generate polynomial roots
+ *  @nroots:	RS code generator polynomial degree (number of roots)
+ */
+struct rs_control *init_rs_non_canonical(int symsize, int (*gffunc)(int),
+                                         int fcr, int prim, int nroots)
+{
+	return init_rs_internal(symsize, 0, gffunc, fcr, prim, nroots);
+}
+
 #ifdef CONFIG_REED_SOLOMON_ENC8
 /**
  *  encode_rs8 - Calculate the parity for data values (8bit data width)
@@ -321,6 +374,7 @@ EXPORT_SYMBOL_GPL(decode_rs16);
 #endif
 
 EXPORT_SYMBOL_GPL(init_rs);
+EXPORT_SYMBOL_GPL(init_rs_non_canonical);
 EXPORT_SYMBOL_GPL(free_rs);
 
 MODULE_LICENSE("GPL");


From linux-mtd at lists.infradead.org  Wed May  2 07:59:03 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 02 May 2007 12:59:03 +0100
Subject: [MTD] block2mtd_paramline[] mustn't be __initdata
Message-ID: <E1HjDU6-00061c-Uv@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=4839f0481d8b985aabd6653ba42cf09e2abcc2bd
Commit:     4839f0481d8b985aabd6653ba42cf09e2abcc2bd
Parent:     048c37b4907ecf0e55100365e20758667f0bd27d
Author:     Adrian Bunk <bunk at stusta.de>
AuthorDate: Wed May 2 12:33:17 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 2 12:33:17 2007 +0100

    [MTD] block2mtd_paramline[] mustn't be __initdata
    
    block2mtd_paramline[] is used in the non-__init block2mtd_setup()
    
    Signed-off-by: Adrian Bunk <bunk at stusta.de>
    Acked-by: Joern Engel <joern at lazybastard.org>
    Cc: <stable at kernel.org>
    Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/devices/block2mtd.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index ce47544..4070720 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -375,7 +375,7 @@ static inline void kill_final_newline(char *str)
 
 #ifndef MODULE
 static int block2mtd_init_called = 0;
-static __initdata char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */
+static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */
 #endif
 
 


From linux-mtd at lists.infradead.org  Wed May  2 07:59:02 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 02 May 2007 12:59:02 +0100
Subject: =?UTF-8?Q?[MTD]_[NAND]_Use_rslib_for_CAF=C3=89_ECC?=
Message-ID: <E1HjDU5-0005zK-O2@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=8c61b7a7f4d4e46be61cf1777361749b2d300c14
Commit:     8c61b7a7f4d4e46be61cf1777361749b2d300c14
Parent:     d7e5a5462f68270ed66efff22b1981be57a28c19
Author:     Segher Boessenkool <segher at kernel.crashing.org>
AuthorDate: Wed May 2 12:18:49 2007 +0200
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 2 12:00:20 2007 +0100

    [MTD] [NAND] Use rslib for CAF? ECC
    
    Signed-off-by: Segher Boessenkool <segher at kernel.crashing.org>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/nand/Kconfig    |   12 +-
 drivers/mtd/nand/Makefile   |    2 +-
 drivers/mtd/nand/cafe.c     |  110 +++-
 drivers/mtd/nand/cafe_ecc.c | 1381 -------------------------------------------
 4 files changed, 106 insertions(+), 1399 deletions(-)

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index d05873b..03ed254 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -232,11 +232,13 @@ config MTD_NAND_BASLER_EXCITE
           will be named "excite_nandflash.ko".
 
 config MTD_NAND_CAFE
-       tristate "NAND support for OLPC CAF? chip"
-       depends on PCI
-       help
-	 Use NAND flash attached to the CAF? chip designed for the $100
-	 laptop.
+	tristate "NAND support for OLPC CAF? chip"
+	depends on PCI
+	select REED_SOLOMON
+	select REED_SOLOMON_DEC16
+	help
+	  Use NAND flash attached to the CAF? chip designed for the $100
+	  laptop.
 
 config MTD_NAND_CS553X
 	tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 6872031..79e9e5d 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -28,4 +28,4 @@ obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)	+= excite_nandflash.o
 
 nand-objs := nand_base.o nand_bbt.o
-cafe_nand-objs := cafe.o cafe_ecc.o
+cafe_nand-objs := cafe.o
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c
index c328a75..05f6ec9 100644
--- a/drivers/mtd/nand/cafe.c
+++ b/drivers/mtd/nand/cafe.c
@@ -11,6 +11,7 @@
 #undef DEBUG
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
+#include <linux/rslib.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -46,13 +47,11 @@
 #define CAFE_GLOBAL_IRQ_MASK	0x300c
 #define CAFE_NAND_RESET		0x3034
 
-int cafe_correct_ecc(unsigned char *buf,
-		     unsigned short *chk_syndrome_list);
-
 struct cafe_priv {
 	struct nand_chip nand;
 	struct pci_dev *pdev;
 	void __iomem *mmio;
+	struct rs_control *rs;
 	uint32_t ctl1;
 	uint32_t ctl2;
 	int datalen;
@@ -374,28 +373,66 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 
 	if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
-		unsigned short syn[8];
-		int i;
+		unsigned short syn[8], pat[4];
+		int pos[4];
+		u8 *oob = chip->oob_poi;
+		int i, n;
 
 		for (i=0; i<8; i+=2) {
 			uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
-			syn[i] = tmp & 0xfff;
-			syn[i+1] = (tmp >> 16) & 0xfff;
+			syn[i] = cafe->rs->index_of[tmp & 0xfff];
+			syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff];
+		}
+
+		n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0,
+		                pat);
+
+		for (i = 0; i < n; i++) {
+			int p = pos[i];
+
+			/* The 12-bit symbols are mapped to bytes here */
+
+			if (p > 1374) {
+				/* out of range */
+				n = -1374;
+			} else if (p == 0) {
+				/* high four bits do not correspond to data */
+				if (pat[i] > 0xff)
+					n = -2048;
+				else
+					buf[0] ^= pat[i];
+			} else if (p == 1365) {
+				buf[2047] ^= pat[i] >> 4;
+				oob[0] ^= pat[i] << 4;
+			} else if (p > 1365) {
+				if ((p & 1) == 1) {
+					oob[3*p/2 - 2048] ^= pat[i] >> 4;
+					oob[3*p/2 - 2047] ^= pat[i] << 4;
+				} else {
+					oob[3*p/2 - 2049] ^= pat[i] >> 8;
+					oob[3*p/2 - 2048] ^= pat[i];
+				}
+			} else if ((p & 1) == 1) {
+				buf[3*p/2] ^= pat[i] >> 4;
+				buf[3*p/2 + 1] ^= pat[i] << 4;
+			} else {
+				buf[3*p/2 - 1] ^= pat[i] >> 8;
+				buf[3*p/2] ^= pat[i];
+			}
 		}
 
-		if ((i = cafe_correct_ecc(buf, syn)) < 0) {
+		if (n < 0) {
 			dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
 				cafe_readl(cafe, NAND_ADDR2) * 2048);
-			for (i=0; i< 0x5c; i+=4)
+			for (i = 0; i < 0x5c; i += 4)
 				printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
 			mtd->ecc_stats.failed++;
 		} else {
-			dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", i);
-			mtd->ecc_stats.corrected += i;
+			dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n);
+			mtd->ecc_stats.corrected += n;
 		}
 	}
 
-
 	return 0;
 }
 
@@ -525,6 +562,48 @@ static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 	return 0;
 }
 
+/* F_2[X]/(X**6+X+1)  */
+static unsigned short __devinit gf64_mul(u8 a, u8 b)
+{
+	u8 c;
+	unsigned int i;
+
+	c = 0;
+	for (i = 0; i < 6; i++) {
+		if (a & 1)
+			c ^= b;
+		a >>= 1;
+		b <<= 1;
+		if ((b & 0x40) != 0)
+			b ^= 0x43;
+	}
+
+	return c;
+}
+
+/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X]  */
+static u16 __devinit gf4096_mul(u16 a, u16 b)
+{
+	u8 ah, al, bh, bl, ch, cl;
+
+	ah = a >> 6;
+	al = a & 0x3f;
+	bh = b >> 6;
+	bl = b & 0x3f;
+
+	ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl);
+	cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl);
+
+	return (ch << 6) ^ cl;
+}
+
+static int __devinit cafe_mul(int x)
+{
+	if (x == 0)
+		return 1;
+	return gf4096_mul(x, 0xe01);
+}
+
 static int __devinit cafe_nand_probe(struct pci_dev *pdev,
 				     const struct pci_device_id *ent)
 {
@@ -564,6 +643,12 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
 	}
 	cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
 
+	cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
+	if (!cafe->rs) {
+		err = -ENOMEM;
+		goto out_ior;
+	}
+
 	cafe->nand.cmdfunc = cafe_nand_cmdfunc;
 	cafe->nand.dev_ready = cafe_device_ready;
 	cafe->nand.read_byte = cafe_read_byte;
@@ -713,6 +798,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev)
 	cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
 	free_irq(pdev->irq, mtd);
 	nand_release(mtd);
+	free_rs(cafe->rs);
 	pci_iounmap(pdev, cafe->mmio);
 	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
 	kfree(mtd);
diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c
deleted file mode 100644
index ea5c849..0000000
--- a/drivers/mtd/nand/cafe_ecc.c
+++ /dev/null
@@ -1,1381 +0,0 @@
-/* Error correction for CAF? NAND controller
- *
- * ? 2006 Marvell, Inc.
- * Author: Tom Chiou
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-
-static unsigned short gf4096_mul(unsigned short, unsigned short);
-static unsigned short gf64_mul(unsigned short, unsigned short);
-static unsigned short gf4096_inv(unsigned short);
-static unsigned short err_pos(unsigned short);
-static void find_4bit_err_coefs(unsigned short, unsigned short, unsigned short,
-				unsigned short, unsigned short, unsigned short,
-				unsigned short, unsigned short, unsigned short *);
-static void zero_4x5_col3(unsigned short[4][5]);
-static void zero_4x5_col2(unsigned short[4][5]);
-static void zero_4x5_col1(unsigned short[4][5]);
-static void swap_4x5_rows(unsigned short[4][5], int, int, int);
-static void swap_2x3_rows(unsigned short m[2][3]);
-static void solve_4x5(unsigned short m[4][5], unsigned short *, int *);
-static void sort_coefs(int *, unsigned short *, int);
-static void find_4bit_err_pats(unsigned short, unsigned short, unsigned short,
-			       unsigned short, unsigned short, unsigned short,
-			       unsigned short, unsigned short, unsigned short *);
-static void find_3bit_err_coefs(unsigned short, unsigned short, unsigned short,
-				unsigned short, unsigned short, unsigned short,
-				unsigned short *);
-static void zero_3x4_col2(unsigned short[3][4]);
-static void zero_3x4_col1(unsigned short[3][4]);
-static void swap_3x4_rows(unsigned short[3][4], int, int, int);
-static void solve_3x4(unsigned short[3][4], unsigned short *, int *);
-static void find_3bit_err_pats(unsigned short, unsigned short, unsigned short,
-			       unsigned short, unsigned short, unsigned short,
-			       unsigned short *);
-
-static void find_2bit_err_pats(unsigned short, unsigned short, unsigned short,
-			       unsigned short, unsigned short *);
-static void find_2x2_soln(unsigned short, unsigned short, unsigned short,
-			  unsigned short, unsigned short, unsigned short,
-			  unsigned short *);
-static void solve_2x3(unsigned short[2][3], unsigned short *);
-static int chk_no_err_only(unsigned short *, unsigned short *);
-static int chk_1_err_only(unsigned short *, unsigned short *);
-static int chk_2_err_only(unsigned short *, unsigned short *);
-static int chk_3_err_only(unsigned short *, unsigned short *);
-static int chk_4_err_only(unsigned short *, unsigned short *);
-
-static unsigned short gf64_mul(unsigned short a, unsigned short b)
-{
-	unsigned short tmp1, tmp2, tmp3, tmp4, tmp5;
-	unsigned short c_bit0, c_bit1, c_bit2, c_bit3, c_bit4, c_bit5, c;
-
-	tmp1 = ((a) ^ (a >> 5));
-	tmp2 = ((a >> 4) ^ (a >> 5));
-	tmp3 = ((a >> 3) ^ (a >> 4));
-	tmp4 = ((a >> 2) ^ (a >> 3));
-	tmp5 = ((a >> 1) ^ (a >> 2));
-
-	c_bit0 = ((a & b) ^ ((a >> 5) & (b >> 1)) ^ ((a >> 4) & (b >> 2)) ^
-		  ((a >> 3) & (b >> 3)) ^ ((a >> 2) & (b >> 4)) ^ ((a >> 1) & (b >> 5))) & 0x1;
-
-	c_bit1 = (((a >> 1) & b) ^ (tmp1 & (b >> 1)) ^ (tmp2 & (b >> 2)) ^
-		  (tmp3 & (b >> 3)) ^ (tmp4 & (b >> 4)) ^ (tmp5 & (b >> 5))) & 0x1;
-
-	c_bit2 = (((a >> 2) & b) ^ ((a >> 1) & (b >> 1)) ^ (tmp1 & (b >> 2)) ^
-		  (tmp2 & (b >> 3)) ^ (tmp3 & (b >> 4)) ^ (tmp4 & (b >> 5))) & 0x1;
-
-	c_bit3 = (((a >> 3) & b) ^ ((a >> 2) & (b >> 1)) ^ ((a >> 1) & (b >> 2)) ^
-		  (tmp1 & (b >> 3)) ^ (tmp2 & (b >> 4)) ^ (tmp3 & (b >> 5))) & 0x1;
-
-	c_bit4 = (((a >> 4) & b) ^ ((a >> 3) & (b >> 1)) ^ ((a >> 2) & (b >> 2)) ^
-		  ((a >> 1) & (b >> 3)) ^ (tmp1 & (b >> 4)) ^ (tmp2 & (b >> 5))) & 0x1;
-
-	c_bit5 = (((a >> 5) & b) ^ ((a >> 4) & (b >> 1)) ^ ((a >> 3) & (b >> 2)) ^
-		  ((a >> 2) & (b >> 3)) ^ ((a >> 1) & (b >> 4)) ^ (tmp1 & (b >> 5))) & 0x1;
-
-	c = c_bit0 | (c_bit1 << 1) | (c_bit2 << 2) | (c_bit3 << 3) | (c_bit4 << 4) | (c_bit5 << 5);
-
-	return c;
-}
-
-static unsigned short gf4096_mul(unsigned short a, unsigned short b)
-{
-	unsigned short ah, al, bh, bl, alxah, blxbh, ablh, albl, ahbh, ahbhB, c;
-
-	ah = (a >> 6) & 0x3f;
-	al = a & 0x3f;
-	bh = (b >> 6) & 0x3f;
-	bl = b & 0x3f;
-	alxah = al ^ ah;
-	blxbh = bl ^ bh;
-
-	ablh = gf64_mul(alxah, blxbh);
-	albl = gf64_mul(al, bl);
-	ahbh = gf64_mul(ah, bh);
-
-	ahbhB = ((ahbh & 0x1) << 5) |
-	    ((ahbh & 0x20) >> 1) |
-	    ((ahbh & 0x10) >> 1) | ((ahbh & 0x8) >> 1) | ((ahbh & 0x4) >> 1) | (((ahbh >> 1) ^ ahbh) & 0x1);
-
-	c = ((ablh ^ albl) << 6) | (ahbhB ^ albl);
-	return c;
-}
-
-static void find_2bit_err_pats(unsigned short s0, unsigned short s1, unsigned short r0, unsigned short r1, unsigned short *pats)
-{
-	find_2x2_soln(0x1, 0x1, r0, r1, s0, s1, pats);
-}
-
-static void find_3bit_err_coefs(unsigned short s0, unsigned short s1,
-				unsigned short s2, unsigned short s3, unsigned short s4, unsigned short s5, unsigned short *coefs)
-{
-	unsigned short m[3][4];
-	int row_order[3];
-
-	row_order[0] = 0;
-	row_order[1] = 1;
-	row_order[2] = 2;
-	m[0][0] = s2;
-	m[0][1] = s1;
-	m[0][2] = s0;
-	m[0][3] = s3;
-	m[1][0] = s3;
-	m[1][1] = s2;
-	m[1][2] = s1;
-	m[1][3] = s4;
-	m[2][0] = s4;
-	m[2][1] = s3;
-	m[2][2] = s2;
-	m[2][3] = s5;
-
-	if (m[0][2] != 0x0) {
-		zero_3x4_col2(m);
-	} else if (m[1][2] != 0x0) {
-		swap_3x4_rows(m, 0, 1, 4);
-		zero_3x4_col2(m);
-	} else if (m[2][2] != 0x0) {
-		swap_3x4_rows(m, 0, 2, 4);
-		zero_3x4_col2(m);
-	} else {
-		printk(KERN_ERR "Error: find_3bit_err_coefs, s0,s1,s2 all zeros!\n");
-	}
-
-	if (m[1][1] != 0x0) {
-		zero_3x4_col1(m);
-	} else if (m[2][1] != 0x0) {
-		swap_3x4_rows(m, 1, 2, 4);
-		zero_3x4_col1(m);
-	} else {
-		printk(KERN_ERR "Error: find_3bit_err_coefs, cannot resolve col 1!\n");
-	}
-
-	/* solve coefs */
-	solve_3x4(m, coefs, row_order);
-}
-
-static void zero_3x4_col2(unsigned short m[3][4])
-{
-	unsigned short minv1, minv2;
-
-	minv1 = gf4096_mul(m[1][2], gf4096_inv(m[0][2]));
-	minv2 = gf4096_mul(m[2][2], gf4096_inv(m[0][2]));
-	m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv1);
-	m[1][1] = m[1][1] ^ gf4096_mul(m[0][1], minv1);
-	m[1][3] = m[1][3] ^ gf4096_mul(m[0][3], minv1);
-	m[2][0] = m[2][0] ^ gf4096_mul(m[0][0], minv2);
-	m[2][1] = m[2][1] ^ gf4096_mul(m[0][1], minv2);
-	m[2][3] = m[2][3] ^ gf4096_mul(m[0][3], minv2);
-}
-
-static void zero_3x4_col1(unsigned short m[3][4])
-{
-	unsigned short minv;
-	minv = gf4096_mul(m[2][1], gf4096_inv(m[1][1]));
-	m[2][0] = m[2][0] ^ gf4096_mul(m[1][0], minv);
-	m[2][3] = m[2][3] ^ gf4096_mul(m[1][3], minv);
-}
-
-static void swap_3x4_rows(unsigned short m[3][4], int i, int j, int col_width)
-{
-	unsigned short tmp0;
-	int cnt;
-	for (cnt = 0; cnt < col_width; cnt++) {
-		tmp0 = m[i][cnt];
-		m[i][cnt] = m[j][cnt];
-		m[j][cnt] = tmp0;
-	}
-}
-
-static void solve_3x4(unsigned short m[3][4], unsigned short *coefs, int *row_order)
-{
-	unsigned short tmp[3];
-	tmp[0] = gf4096_mul(m[2][3], gf4096_inv(m[2][0]));
-	tmp[1] = gf4096_mul((gf4096_mul(tmp[0], m[1][0]) ^ m[1][3]), gf4096_inv(m[1][1]));
-	tmp[2] = gf4096_mul((gf4096_mul(tmp[0], m[0][0]) ^ gf4096_mul(tmp[1], m[0][1]) ^ m[0][3]), gf4096_inv(m[0][2]));
-	sort_coefs(row_order, tmp, 3);
-	coefs[0] = tmp[0];
-	coefs[1] = tmp[1];
-	coefs[2] = tmp[2];
-}
-
-static void find_3bit_err_pats(unsigned short s0, unsigned short s1,
-			       unsigned short s2, unsigned short r0,
-			       unsigned short r1, unsigned short r2,
-			       unsigned short *pats)
-{
-	find_2x2_soln(r0 ^ r2, r1 ^ r2,
-		      gf4096_mul(r0, r0 ^ r2), gf4096_mul(r1, r1 ^ r2),
-		      gf4096_mul(s0, r2) ^ s1, gf4096_mul(s1, r2) ^ s2, pats);
-	pats[2] = s0 ^ pats[0] ^ pats[1];
-}
-
-static void find_4bit_err_coefs(unsigned short s0, unsigned short s1,
-				unsigned short s2, unsigned short s3,
-				unsigned short s4, unsigned short s5,
-				unsigned short s6, unsigned short s7,
-				unsigned short *coefs)
-{
-	unsigned short m[4][5];
-	int row_order[4];
-
-	row_order[0] = 0;
-	row_order[1] = 1;
-	row_order[2] = 2;
-	row_order[3] = 3;
-
-	m[0][0] = s3;
-	m[0][1] = s2;
-	m[0][2] = s1;
-	m[0][3] = s0;
-	m[0][4] = s4;
-	m[1][0] = s4;
-	m[1][1] = s3;
-	m[1][2] = s2;
-	m[1][3] = s1;
-	m[1][4] = s5;
-	m[2][0] = s5;
-	m[2][1] = s4;
-	m[2][2] = s3;
-	m[2][3] = s2;
-	m[2][4] = s6;
-	m[3][0] = s6;
-	m[3][1] = s5;
-	m[3][2] = s4;
-	m[3][3] = s3;
-	m[3][4] = s7;
-
-	if (m[0][3] != 0x0) {
-		zero_4x5_col3(m);
-	} else if (m[1][3] != 0x0) {
-		swap_4x5_rows(m, 0, 1, 5);
-		zero_4x5_col3(m);
-	} else if (m[2][3] != 0x0) {
-		swap_4x5_rows(m, 0, 2, 5);
-		zero_4x5_col3(m);
-	} else if (m[3][3] != 0x0) {
-		swap_4x5_rows(m, 0, 3, 5);
-		zero_4x5_col3(m);
-	} else {
-		printk(KERN_ERR "Error: find_4bit_err_coefs, s0,s1,s2,s3 all zeros!\n");
-	}
-
-	if (m[1][2] != 0x0) {
-		zero_4x5_col2(m);
-	} else if (m[2][2] != 0x0) {
-		swap_4x5_rows(m, 1, 2, 5);
-		zero_4x5_col2(m);
-	} else if (m[3][2] != 0x0) {
-		swap_4x5_rows(m, 1, 3, 5);
-		zero_4x5_col2(m);
-	} else {
-		printk(KERN_ERR "Error: find_4bit_err_coefs, cannot resolve col 2!\n");
-	}
-
-	if (m[2][1] != 0x0) {
-		zero_4x5_col1(m);
-	} else if (m[3][1] != 0x0) {
-		swap_4x5_rows(m, 2, 3, 5);
-		zero_4x5_col1(m);
-	} else {
-		printk(KERN_ERR "Error: find_4bit_err_coefs, cannot resolve col 1!\n");
-	}
-
-	solve_4x5(m, coefs, row_order);
-}
-
-static void zero_4x5_col3(unsigned short m[4][5])
-{
-	unsigned short minv1, minv2, minv3;
-
-	minv1 = gf4096_mul(m[1][3], gf4096_inv(m[0][3]));
-	minv2 = gf4096_mul(m[2][3], gf4096_inv(m[0][3]));
-	minv3 = gf4096_mul(m[3][3], gf4096_inv(m[0][3]));
-
-	m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv1);
-	m[1][1] = m[1][1] ^ gf4096_mul(m[0][1], minv1);
-	m[1][2] = m[1][2] ^ gf4096_mul(m[0][2], minv1);
-	m[1][4] = m[1][4] ^ gf4096_mul(m[0][4], minv1);
-	m[2][0] = m[2][0] ^ gf4096_mul(m[0][0], minv2);
-	m[2][1] = m[2][1] ^ gf4096_mul(m[0][1], minv2);
-	m[2][2] = m[2][2] ^ gf4096_mul(m[0][2], minv2);
-	m[2][4] = m[2][4] ^ gf4096_mul(m[0][4], minv2);
-	m[3][0] = m[3][0] ^ gf4096_mul(m[0][0], minv3);
-	m[3][1] = m[3][1] ^ gf4096_mul(m[0][1], minv3);
-	m[3][2] = m[3][2] ^ gf4096_mul(m[0][2], minv3);
-	m[3][4] = m[3][4] ^ gf4096_mul(m[0][4], minv3);
-}
-
-static void zero_4x5_col2(unsigned short m[4][5])
-{
-	unsigned short minv2, minv3;
-
-	minv2 = gf4096_mul(m[2][2], gf4096_inv(m[1][2]));
-	minv3 = gf4096_mul(m[3][2], gf4096_inv(m[1][2]));
-
-	m[2][0] = m[2][0] ^ gf4096_mul(m[1][0], minv2);
-	m[2][1] = m[2][1] ^ gf4096_mul(m[1][1], minv2);
-	m[2][4] = m[2][4] ^ gf4096_mul(m[1][4], minv2);
-	m[3][0] = m[3][0] ^ gf4096_mul(m[1][0], minv3);
-	m[3][1] = m[3][1] ^ gf4096_mul(m[1][1], minv3);
-	m[3][4] = m[3][4] ^ gf4096_mul(m[1][4], minv3);
-}
-
-static void zero_4x5_col1(unsigned short m[4][5])
-{
-	unsigned short minv;
-
-	minv = gf4096_mul(m[3][1], gf4096_inv(m[2][1]));
-
-	m[3][0] = m[3][0] ^ gf4096_mul(m[2][0], minv);
-	m[3][4] = m[3][4] ^ gf4096_mul(m[2][4], minv);
-}
-
-static void swap_4x5_rows(unsigned short m[4][5], int i, int j, int col_width)
-{
-	unsigned short tmp0;
-	int cnt;
-
-	for (cnt = 0; cnt < col_width; cnt++) {
-		tmp0 = m[i][cnt];
-		m[i][cnt] = m[j][cnt];
-		m[j][cnt] = tmp0;
-	}
-}
-
-static void solve_4x5(unsigned short m[4][5], unsigned short *coefs, int *row_order)
-{
-	unsigned short tmp[4];
-
-	tmp[0] = gf4096_mul(m[3][4], gf4096_inv(m[3][0]));
-	tmp[1] = gf4096_mul((gf4096_mul(tmp[0], m[2][0]) ^ m[2][4]), gf4096_inv(m[2][1]));
-	tmp[2] = gf4096_mul((gf4096_mul(tmp[0], m[1][0]) ^ gf4096_mul(tmp[1], m[1][1]) ^ m[1][4]), gf4096_inv(m[1][2]));
-	tmp[3] = gf4096_mul((gf4096_mul(tmp[0], m[0][0]) ^
-			gf4096_mul(tmp[1], m[0][1]) ^ gf4096_mul(tmp[2], m[0][2]) ^ m[0][4]), gf4096_inv(m[0][3]));
-	sort_coefs(row_order, tmp, 4);
-	coefs[0] = tmp[0];
-	coefs[1] = tmp[1];
-	coefs[2] = tmp[2];
-	coefs[3] = tmp[3];
-}
-
-static void sort_coefs(int *order, unsigned short *soln, int len)
-{
-	int cnt, start_cnt, least_ord, least_cnt;
-	unsigned short tmp0;
-	for (start_cnt = 0; start_cnt < len; start_cnt++) {
-		for (cnt = start_cnt; cnt < len; cnt++) {
-			if (cnt == start_cnt) {
-				least_ord = order[cnt];
-				least_cnt = start_cnt;
-			} else {
-				if (least_ord > order[cnt]) {
-					least_ord = order[cnt];
-					least_cnt = cnt;
-				}
-			}
-		}
-		if (least_cnt != start_cnt) {
-			tmp0 = order[least_cnt];
-			order[least_cnt] = order[start_cnt];
-			order[start_cnt] = tmp0;
-			tmp0 = soln[least_cnt];
-			soln[least_cnt] = soln[start_cnt];
-			soln[start_cnt] = tmp0;
-		}
-	}
-}
-
-static void find_4bit_err_pats(unsigned short s0, unsigned short s1,
-			       unsigned short s2, unsigned short s3,
-			       unsigned short z1, unsigned short z2,
-			       unsigned short z3, unsigned short z4,
-			       unsigned short *pats)
-{
-	unsigned short z4_z1, z3z4_z3z3, z4_z2, s0z4_s1, z1z4_z1z1,
-		z4_z3, z2z4_z2z2, s1z4_s2, z3z3z4_z3z3z3, z1z1z4_z1z1z1, z2z2z4_z2z2z2, s2z4_s3;
-	unsigned short tmp0, tmp1, tmp2, tmp3;
-
-	z4_z1 = z4 ^ z1;
-	z3z4_z3z3 = gf4096_mul(z3, z4) ^ gf4096_mul(z3, z3);
-	z4_z2 = z4 ^ z2;
-	s0z4_s1 = gf4096_mul(s0, z4) ^ s1;
-	z1z4_z1z1 = gf4096_mul(z1, z4) ^ gf4096_mul(z1, z1);
-	z4_z3 = z4 ^ z3;
-	z2z4_z2z2 = gf4096_mul(z2, z4) ^ gf4096_mul(z2, z2);
-	s1z4_s2 = gf4096_mul(s1, z4) ^ s2;
-	z3z3z4_z3z3z3 = gf4096_mul(gf4096_mul(z3, z3), z4) ^ gf4096_mul(gf4096_mul(z3, z3), z3);
-	z1z1z4_z1z1z1 = gf4096_mul(gf4096_mul(z1, z1), z4) ^ gf4096_mul(gf4096_mul(z1, z1), z1);
-	z2z2z4_z2z2z2 = gf4096_mul(gf4096_mul(z2, z2), z4) ^ gf4096_mul(gf4096_mul(z2, z2), z2);
-	s2z4_s3 = gf4096_mul(s2, z4) ^ s3;
-
-	//find err pat 0,1
-	find_2x2_soln(gf4096_mul(z4_z1, z3z4_z3z3) ^
-		      gf4096_mul(z1z4_z1z1, z4_z3), gf4096_mul(z4_z2,
-							       z3z4_z3z3) ^
-		      gf4096_mul(z2z4_z2z2, z4_z3), gf4096_mul(z1z4_z1z1,
-							       z3z3z4_z3z3z3) ^
-		      gf4096_mul(z1z1z4_z1z1z1, z3z4_z3z3),
-		      gf4096_mul(z2z4_z2z2,
-				 z3z3z4_z3z3z3) ^ gf4096_mul(z2z2z4_z2z2z2,
-							     z3z4_z3z3),
-		      gf4096_mul(s0z4_s1, z3z4_z3z3) ^ gf4096_mul(s1z4_s2,
-								  z4_z3),
-		      gf4096_mul(s1z4_s2, z3z3z4_z3z3z3) ^ gf4096_mul(s2z4_s3, z3z4_z3z3), pats);
-	tmp0 = pats[0];
-	tmp1 = pats[1];
-	tmp2 = pats[0] ^ pats[1] ^ s0;
-	tmp3 = gf4096_mul(pats[0], z1) ^ gf4096_mul(pats[1], z2) ^ s1;
-
-	//find err pat 2,3
-	find_2x2_soln(0x1, 0x1, z3, z4, tmp2, tmp3, pats);
-	pats[2] = pats[0];
-	pats[3] = pats[1];
-	pats[0] = tmp0;
-	pats[1] = tmp1;
-}
-
-static void find_2x2_soln(unsigned short c00, unsigned short c01,
-			  unsigned short c10, unsigned short c11,
-			  unsigned short lval0, unsigned short lval1,
-			  unsigned short *soln)
-{
-	unsigned short m[2][3];
-	m[0][0] = c00;
-	m[0][1] = c01;
-	m[0][2] = lval0;
-	m[1][0] = c10;
-	m[1][1] = c11;
-	m[1][2] = lval1;
-
-	if (m[0][1] != 0x0) {
-		/* */
-	} else if (m[1][1] != 0x0) {
-		swap_2x3_rows(m);
-	} else {
-		printk(KERN_ERR "Warning: find_2bit_err_coefs, s0,s1 all zeros!\n");
-	}
-
-	solve_2x3(m, soln);
-}
-
-static void swap_2x3_rows(unsigned short m[2][3])
-{
-	unsigned short tmp0;
-	int cnt;
-
-	for (cnt = 0; cnt < 3; cnt++) {
-		tmp0 = m[0][cnt];
-		m[0][cnt] = m[1][cnt];
-		m[1][cnt] = tmp0;
-	}
-}
-
-static void solve_2x3(unsigned short m[2][3], unsigned short *coefs)
-{
-	unsigned short minv;
-
-	minv = gf4096_mul(m[1][1], gf4096_inv(m[0][1]));
-	m[1][0] = m[1][0] ^ gf4096_mul(m[0][0], minv);
-	m[1][2] = m[1][2] ^ gf4096_mul(m[0][2], minv);
-	coefs[0] = gf4096_mul(m[1][2], gf4096_inv(m[1][0]));
-	coefs[1] = gf4096_mul((gf4096_mul(coefs[0], m[0][0]) ^ m[0][2]), gf4096_inv(m[0][1]));
-}
-
-static unsigned char gf64_inv[64] = {
-	 0,  1, 33, 62, 49, 43, 31, 44, 57, 37, 52, 28, 46, 40, 22, 25,
-	61, 54, 51, 39, 26, 35, 14, 24, 23, 15, 20, 34, 11, 53, 45,  6,
-	63,  2, 27, 21, 56,  9, 50, 19, 13, 47, 48,  5,  7, 30, 12, 41,
-	42,  4, 38, 18, 10, 29, 17, 60, 36,  8, 59, 58, 55, 16,  3, 32
-};
-
-static unsigned short gf4096_inv(unsigned short din)
-{
-	unsigned short alahxal, ah2B, deno, inv, bl, bh;
-	unsigned short ah, al, ahxal;
-	unsigned short dout;
-
-	ah = (din >> 6) & 0x3f;
-	al = din & 0x3f;
-	ahxal = ah ^ al;
-	ah2B = (((ah ^ (ah >> 3)) & 0x1) << 5) |
-		((ah >> 1) & 0x10) |
-		((((ah >> 5) ^ (ah >> 2)) & 0x1) << 3) |
-		((ah >> 2) & 0x4) | ((((ah >> 4) ^ (ah >> 1)) & 0x1) << 1) | (ah & 0x1);
-	alahxal = gf64_mul(ahxal, al);
-	deno = alahxal ^ ah2B;
-	inv = gf64_inv[deno];
-	bl = gf64_mul(inv, ahxal);
-	bh = gf64_mul(inv, ah);
-	dout = ((bh & 0x3f) << 6) | (bl & 0x3f);
-	return (((bh & 0x3f) << 6) | (bl & 0x3f));
-}
-
-static unsigned short err_pos_lut[4096] = {
-	0xfff, 0x000, 0x451, 0xfff, 0xfff, 0x3cf, 0xfff, 0x041,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x28a, 0xfff, 0x492, 0xfff,
-	0x145, 0xfff, 0xfff, 0x514, 0xfff, 0x082, 0xfff, 0xfff,
-	0xfff, 0x249, 0x38e, 0x410, 0xfff, 0x104, 0x208, 0x1c7,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x2cb, 0xfff, 0xfff, 0xfff,
-	0x0c3, 0x34d, 0x4d3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x186, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x30c, 0x555, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x166, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x385, 0x14e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e1,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x538, 0xfff, 0x16d, 0xfff,
-	0xfff, 0xfff, 0x45b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x29c, 0x2cc, 0x30b, 0x2b3, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0b3, 0xfff, 0x2f7,
-	0xfff, 0x32b, 0xfff, 0xfff, 0xfff, 0xfff, 0x0a7, 0xfff,
-	0xfff, 0x2da, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x07e, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x11c, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x22f, 0xfff, 0x1f4, 0xfff, 0xfff,
-	0x2b0, 0x504, 0xfff, 0x114, 0xfff, 0xfff, 0xfff, 0x21d,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x00d, 0x3c4, 0x340, 0x10f,
-	0xfff, 0xfff, 0x266, 0x02e, 0xfff, 0xfff, 0xfff, 0x4f8,
-	0x337, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x07b, 0x168, 0xfff, 0xfff, 0x0fe,
-	0xfff, 0xfff, 0x51a, 0xfff, 0x458, 0xfff, 0x36d, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x073, 0x37d, 0x415, 0x550, 0xfff,
-	0xfff, 0xfff, 0x23b, 0x4b4, 0xfff, 0xfff, 0xfff, 0x1a1,
-	0xfff, 0xfff, 0x3aa, 0xfff, 0x117, 0x04d, 0x341, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x518, 0x03e, 0x0f2, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x363, 0xfff, 0x0b9, 0xfff, 0xfff,
-	0x241, 0xfff, 0xfff, 0x049, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x15f, 0x52d, 0xfff, 0xfff, 0xfff, 0x29e, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x4cf, 0x0fc, 0xfff, 0x36f, 0x3d3, 0xfff,
-	0x228, 0xfff, 0xfff, 0x45e, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x238, 0xfff, 0xfff, 0xfff, 0xfff, 0x47f, 0xfff, 0xfff,
-	0x43a, 0x265, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e8,
-	0xfff, 0xfff, 0x01a, 0xfff, 0xfff, 0xfff, 0xfff, 0x21e,
-	0x1fc, 0x40b, 0xfff, 0xfff, 0xfff, 0x2d0, 0x159, 0xfff,
-	0xfff, 0x313, 0xfff, 0xfff, 0x05c, 0x4cc, 0xfff, 0xfff,
-	0x0f6, 0x3d5, 0xfff, 0xfff, 0xfff, 0x54f, 0xfff, 0xfff,
-	0xfff, 0x172, 0x1e4, 0x07c, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x53c, 0x1ad, 0x535,
-	0x19b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x092, 0xfff, 0x2be, 0xfff, 0xfff, 0x482,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0e6, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x476, 0xfff, 0x51d, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x342, 0x2b5, 0x22e, 0x09a, 0xfff, 0x08d,
-	0x44f, 0x3ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d1, 0xfff,
-	0xfff, 0x543, 0xfff, 0x48f, 0xfff, 0x3d2, 0xfff, 0x0d5,
-	0x113, 0x0ec, 0x427, 0xfff, 0xfff, 0xfff, 0x4c4, 0xfff,
-	0xfff, 0x50a, 0xfff, 0x144, 0xfff, 0x105, 0x39f, 0x294,
-	0x164, 0xfff, 0x31a, 0xfff, 0xfff, 0x49a, 0xfff, 0x130,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x1be, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x49e, 0x371, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x0e8, 0x49c, 0x0f4, 0xfff,
-	0x338, 0x1a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x36c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x1ae, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x31b, 0xfff, 0xfff, 0x2dd, 0x522, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f4,
-	0x3c6, 0x30d, 0xfff, 0xfff, 0xfff, 0xfff, 0x34c, 0x18f,
-	0x30a, 0xfff, 0x01f, 0x079, 0xfff, 0xfff, 0x54d, 0x46b,
-	0x28c, 0x37f, 0xfff, 0xfff, 0xfff, 0xfff, 0x355, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x14f, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x359, 0x3fe, 0x3c5, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x423, 0xfff, 0xfff, 0x34a, 0x22c, 0xfff,
-	0x25a, 0xfff, 0xfff, 0x4ad, 0xfff, 0x28d, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x547, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x2e2, 0xfff, 0xfff, 0x1d5, 0xfff, 0x2a8, 0xfff, 0xfff,
-	0x03f, 0xfff, 0xfff, 0xfff, 0xfff, 0x3eb, 0x0fa, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x55b, 0xfff,
-	0x08e, 0xfff, 0x3ae, 0xfff, 0x3a4, 0xfff, 0x282, 0x158,
-	0xfff, 0x382, 0xfff, 0xfff, 0x499, 0xfff, 0xfff, 0x08a,
-	0xfff, 0xfff, 0xfff, 0x456, 0x3be, 0xfff, 0x1e2, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x559, 0xfff, 0x1a0, 0xfff,
-	0xfff, 0x0b4, 0xfff, 0xfff, 0xfff, 0x2df, 0xfff, 0xfff,
-	0xfff, 0x07f, 0x4f5, 0xfff, 0xfff, 0x27c, 0x133, 0x017,
-	0xfff, 0x3fd, 0xfff, 0xfff, 0xfff, 0x44d, 0x4cd, 0x17a,
-	0x0d7, 0x537, 0xfff, 0xfff, 0x353, 0xfff, 0xfff, 0x351,
-	0x366, 0xfff, 0x44a, 0xfff, 0x1a6, 0xfff, 0xfff, 0xfff,
-	0x291, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1e3,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x389, 0xfff, 0x07a, 0xfff,
-	0x1b6, 0x2ed, 0xfff, 0xfff, 0xfff, 0xfff, 0x24e, 0x074,
-	0xfff, 0xfff, 0x3dc, 0xfff, 0x4e3, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x4eb, 0xfff, 0xfff, 0x3b8, 0x4de, 0xfff, 0x19c,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x262,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x076, 0x4e8, 0x3da,
-	0xfff, 0x531, 0xfff, 0xfff, 0x14a, 0xfff, 0x0a2, 0x433,
-	0x3df, 0x1e9, 0xfff, 0xfff, 0xfff, 0xfff, 0x3e7, 0x285,
-	0x2d8, 0xfff, 0xfff, 0xfff, 0x349, 0x18d, 0x098, 0xfff,
-	0x0df, 0x4bf, 0xfff, 0xfff, 0x0b2, 0xfff, 0x346, 0x24d,
-	0xfff, 0xfff, 0xfff, 0x24f, 0x4fa, 0x2f9, 0xfff, 0xfff,
-	0x3c9, 0xfff, 0x2b4, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x056, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x179, 0xfff, 0x0e9, 0x3f0, 0x33d, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x1fd, 0xfff, 0xfff, 0x526, 0xfff,
-	0xfff, 0xfff, 0x53d, 0xfff, 0xfff, 0xfff, 0x170, 0x331,
-	0xfff, 0x068, 0xfff, 0xfff, 0xfff, 0x3f7, 0xfff, 0x3d8,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x09f, 0x556, 0xfff, 0xfff, 0x02d, 0xfff, 0xfff,
-	0x553, 0xfff, 0xfff, 0xfff, 0x1f0, 0xfff, 0xfff, 0x4d6,
-	0x41e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d5, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x248, 0xfff, 0xfff, 0xfff, 0x0a3,
-	0xfff, 0x217, 0xfff, 0xfff, 0xfff, 0x4f1, 0x209, 0xfff,
-	0xfff, 0x475, 0x234, 0x52b, 0x398, 0xfff, 0x08b, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x2c2, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x268, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x4a3, 0xfff, 0x0aa, 0xfff, 0x1d9, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x155, 0xfff, 0xfff, 0xfff, 0xfff, 0x0bf,
-	0x539, 0xfff, 0xfff, 0x2f1, 0x545, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x2a7, 0x06f, 0xfff, 0x378, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x25e, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x15d, 0x02a, 0xfff, 0xfff, 0x0bc,
-	0x235, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x150, 0xfff, 0x1a9, 0xfff, 0xfff, 0xfff, 0xfff, 0x381,
-	0xfff, 0x04e, 0x270, 0x13f, 0xfff, 0xfff, 0x405, 0xfff,
-	0x3cd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x2ef, 0xfff, 0x06a, 0xfff, 0xfff, 0xfff, 0x34f,
-	0x212, 0xfff, 0xfff, 0x0e2, 0xfff, 0x083, 0x298, 0xfff,
-	0xfff, 0xfff, 0x0c2, 0xfff, 0xfff, 0x52e, 0xfff, 0x488,
-	0xfff, 0xfff, 0xfff, 0x36b, 0xfff, 0xfff, 0xfff, 0x442,
-	0x091, 0xfff, 0x41c, 0xfff, 0xfff, 0x3a5, 0xfff, 0x4e6,
-	0xfff, 0xfff, 0x40d, 0x31d, 0xfff, 0xfff, 0xfff, 0x4c1,
-	0x053, 0xfff, 0x418, 0x13c, 0xfff, 0x350, 0xfff, 0x0ae,
-	0xfff, 0xfff, 0x41f, 0xfff, 0x470, 0xfff, 0x4ca, 0xfff,
-	0xfff, 0xfff, 0x02b, 0x450, 0xfff, 0x1f8, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x293, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x411, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x0b8, 0xfff, 0xfff, 0xfff,
-	0x3e1, 0xfff, 0xfff, 0xfff, 0xfff, 0x43c, 0xfff, 0x2b2,
-	0x2ab, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ec,
-	0xfff, 0xfff, 0xfff, 0x3f8, 0x034, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x11a, 0xfff, 0x541, 0x45c, 0x134,
-	0x1cc, 0xfff, 0xfff, 0xfff, 0x469, 0xfff, 0xfff, 0x44b,
-	0x161, 0xfff, 0xfff, 0xfff, 0x055, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x307, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d1, 0xfff,
-	0xfff, 0xfff, 0x124, 0x37b, 0x26b, 0x336, 0xfff, 0xfff,
-	0x2e4, 0x3cb, 0xfff, 0xfff, 0x0f8, 0x3c8, 0xfff, 0xfff,
-	0xfff, 0x461, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4b5,
-	0x2cf, 0xfff, 0xfff, 0xfff, 0x20f, 0xfff, 0x35a, 0xfff,
-	0x490, 0xfff, 0x185, 0xfff, 0xfff, 0xfff, 0xfff, 0x42e,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x54b, 0xfff, 0xfff, 0xfff,
-	0x146, 0xfff, 0x412, 0xfff, 0xfff, 0xfff, 0x1ff, 0xfff,
-	0xfff, 0x3e0, 0xfff, 0xfff, 0xfff, 0xfff, 0x2d5, 0xfff,
-	0x4df, 0x505, 0xfff, 0x413, 0xfff, 0x1a5, 0xfff, 0x3b2,
-	0xfff, 0xfff, 0xfff, 0x35b, 0xfff, 0x116, 0xfff, 0xfff,
-	0x171, 0x4d0, 0xfff, 0x154, 0x12d, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x468, 0x4db, 0xfff,
-	0xfff, 0x1df, 0xfff, 0xfff, 0xfff, 0xfff, 0x05a, 0xfff,
-	0x0f1, 0x403, 0xfff, 0x22b, 0x2e0, 0xfff, 0xfff, 0xfff,
-	0x2b7, 0x373, 0xfff, 0xfff, 0xfff, 0xfff, 0x13e, 0xfff,
-	0xfff, 0xfff, 0x0d0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x329, 0x1d2, 0x3fa, 0x047, 0xfff, 0x2f2, 0xfff, 0xfff,
-	0x141, 0x0ac, 0x1d7, 0xfff, 0x07d, 0xfff, 0xfff, 0xfff,
-	0x1c1, 0xfff, 0x487, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x045, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x288, 0x0cd, 0xfff, 0xfff, 0xfff, 0xfff, 0x226, 0x1d8,
-	0xfff, 0x153, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4cb,
-	0x528, 0xfff, 0xfff, 0xfff, 0x20a, 0x343, 0x3a1, 0xfff,
-	0xfff, 0xfff, 0x2d7, 0x2d3, 0x1aa, 0x4c5, 0xfff, 0xfff,
-	0xfff, 0x42b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x3e9, 0xfff, 0x20b, 0x260,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x37c, 0x2fd,
-	0xfff, 0xfff, 0x2c8, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x31e, 0xfff, 0x335, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x135, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x35c, 0x4dd, 0x129, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x1ef, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x34e, 0xfff, 0xfff, 0xfff, 0xfff, 0x407, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x3ad, 0xfff, 0xfff, 0xfff,
-	0x379, 0xfff, 0xfff, 0x1d0, 0x38d, 0xfff, 0xfff, 0x1e8,
-	0x184, 0x3c1, 0x1c4, 0xfff, 0x1f9, 0xfff, 0xfff, 0x424,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x1d3, 0x0d4, 0xfff, 0x4e9,
-	0xfff, 0xfff, 0xfff, 0x530, 0x107, 0xfff, 0x106, 0x04f,
-	0xfff, 0xfff, 0x4c7, 0x503, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x15c, 0xfff, 0x23f, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x4f3, 0xfff, 0xfff, 0x3c7,
-	0xfff, 0x278, 0xfff, 0xfff, 0x0a6, 0xfff, 0xfff, 0xfff,
-	0x122, 0x1cf, 0xfff, 0x327, 0xfff, 0x2e5, 0xfff, 0x29d,
-	0xfff, 0xfff, 0x3f1, 0xfff, 0xfff, 0x48d, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x054, 0xfff, 0xfff, 0xfff, 0xfff, 0x178,
-	0x27e, 0x4e0, 0x352, 0x02f, 0x09c, 0xfff, 0x2a0, 0xfff,
-	0xfff, 0x46a, 0x457, 0xfff, 0xfff, 0x501, 0xfff, 0x2ba,
-	0xfff, 0xfff, 0xfff, 0x54e, 0x2e7, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x551, 0xfff, 0xfff, 0x1db, 0x2aa, 0xfff,
-	0xfff, 0x4bc, 0xfff, 0xfff, 0x395, 0xfff, 0x0de, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x455, 0xfff, 0x17e,
-	0xfff, 0x221, 0x4a7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x388, 0xfff, 0xfff, 0xfff, 0x308, 0xfff, 0xfff, 0xfff,
-	0x20e, 0x4b9, 0xfff, 0x273, 0x20c, 0x09e, 0xfff, 0x057,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x3f2, 0xfff, 0x1a8, 0x3a6,
-	0x14c, 0xfff, 0xfff, 0x071, 0xfff, 0xfff, 0x53a, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x109, 0xfff, 0xfff, 0x399, 0xfff,
-	0x061, 0x4f0, 0x39e, 0x244, 0xfff, 0x035, 0xfff, 0xfff,
-	0x305, 0x47e, 0x297, 0xfff, 0xfff, 0x2b8, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1bc, 0xfff, 0x2fc,
-	0xfff, 0xfff, 0x554, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b6,
-	0xfff, 0xfff, 0xfff, 0x515, 0x397, 0xfff, 0xfff, 0x12f,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e5,
-	0xfff, 0x4fc, 0xfff, 0xfff, 0x05e, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x0a8, 0x3af, 0x015, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x138, 0xfff, 0xfff, 0xfff, 0x540, 0xfff, 0xfff,
-	0xfff, 0x027, 0x523, 0x2f0, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x16c, 0xfff, 0x27d, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x04c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4dc,
-	0xfff, 0xfff, 0x059, 0x301, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x1a3, 0xfff, 0x15a, 0xfff, 0xfff,
-	0x0a5, 0xfff, 0x435, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x051, 0xfff, 0xfff, 0x131, 0xfff, 0x4f4, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x441, 0xfff, 0x4fb, 0xfff, 0x03b,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ed, 0x274,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d3, 0x55e, 0x1b3,
-	0xfff, 0x0bd, 0xfff, 0xfff, 0xfff, 0xfff, 0x225, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x4b7, 0xfff, 0xfff, 0x2ff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c3, 0xfff,
-	0x383, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2f6,
-	0xfff, 0xfff, 0x1ee, 0xfff, 0x03d, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x26f, 0x1dc, 0xfff, 0x0db, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x0ce, 0xfff, 0xfff, 0x127, 0x03a,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x311, 0xfff,
-	0xfff, 0x13d, 0x09d, 0x47b, 0x2a6, 0x50d, 0x510, 0x19a,
-	0xfff, 0x354, 0x414, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x44c, 0x3b0, 0xfff, 0x23d, 0x429, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x4c0, 0x416, 0xfff, 0x05b, 0xfff, 0xfff, 0x137, 0xfff,
-	0x25f, 0x49f, 0xfff, 0x279, 0x013, 0xfff, 0xfff, 0xfff,
-	0x269, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3d0, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x077, 0xfff, 0xfff, 0x3fb,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x271, 0x3a0, 0xfff, 0xfff,
-	0x40f, 0xfff, 0xfff, 0x3de, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ab, 0x26a,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x489, 0xfff, 0xfff,
-	0x252, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b7, 0x42f, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x3b7,
-	0xfff, 0x2bb, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x0f7, 0x01d, 0xfff, 0x067, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x4e2, 0xfff, 0xfff, 0x4bb, 0xfff,
-	0xfff, 0xfff, 0x17b, 0xfff, 0x0ee, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x36e, 0xfff, 0xfff, 0xfff, 0x533, 0xfff,
-	0xfff, 0xfff, 0x4d4, 0x356, 0xfff, 0xfff, 0x375, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x4a4, 0x513, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4ff, 0xfff, 0x2af,
-	0xfff, 0xfff, 0x026, 0xfff, 0x0ad, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x26e, 0xfff, 0xfff, 0xfff, 0xfff, 0x493, 0xfff,
-	0x463, 0x4d2, 0x4be, 0xfff, 0xfff, 0xfff, 0xfff, 0x4f2,
-	0x0b6, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x32d, 0x315, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x13a, 0x4a1, 0xfff, 0x27a, 0xfff, 0xfff, 0xfff,
-	0x47a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x334, 0xfff, 0xfff, 0xfff, 0xfff, 0x54c, 0xfff, 0xfff,
-	0xfff, 0x0c9, 0x007, 0xfff, 0xfff, 0x12e, 0xfff, 0x0ff,
-	0xfff, 0xfff, 0x3f5, 0x509, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x1c3, 0x2ad, 0xfff, 0xfff, 0x47c, 0x261, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x152, 0xfff, 0xfff, 0xfff, 0x339,
-	0xfff, 0x243, 0x1c0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x063, 0xfff, 0xfff, 0x254, 0xfff, 0xfff, 0x173, 0xfff,
-	0x0c7, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x362, 0x259, 0x485, 0x374, 0x0dc, 0x3ab, 0xfff,
-	0x1c5, 0x534, 0x544, 0xfff, 0xfff, 0x508, 0xfff, 0x402,
-	0x408, 0xfff, 0x0e7, 0xfff, 0xfff, 0x00a, 0x205, 0xfff,
-	0xfff, 0x2b9, 0xfff, 0xfff, 0xfff, 0x465, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x23a, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x147, 0x19d, 0x115, 0x214, 0xfff, 0x090, 0x368,
-	0xfff, 0x210, 0xfff, 0xfff, 0x280, 0x52a, 0x163, 0x148,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x326, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x2de, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x206, 0x2c1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x189, 0xfff, 0xfff, 0xfff, 0xfff, 0x367, 0xfff, 0x1a4,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x443, 0xfff, 0x27b,
-	0xfff, 0xfff, 0x251, 0x549, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x188, 0x04b, 0xfff, 0xfff, 0xfff, 0x31f,
-	0x4a6, 0xfff, 0x246, 0x1de, 0x156, 0xfff, 0xfff, 0xfff,
-	0x3a9, 0xfff, 0xfff, 0xfff, 0x2fa, 0xfff, 0x128, 0x0d1,
-	0x449, 0x255, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x258, 0xfff, 0xfff, 0xfff,
-	0x532, 0xfff, 0xfff, 0xfff, 0x303, 0x517, 0xfff, 0xfff,
-	0x2a9, 0x24a, 0xfff, 0xfff, 0x231, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x4b6, 0x516, 0xfff, 0xfff, 0x0e4, 0x0eb,
-	0xfff, 0x4e4, 0xfff, 0x275, 0xfff, 0xfff, 0x031, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x025, 0x21a, 0xfff, 0x0cc,
-	0x45f, 0x3d9, 0x289, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x23e, 0xfff, 0xfff, 0xfff, 0x438, 0x097,
-	0x419, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x0a9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x37e, 0x0e0, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x431,
-	0x372, 0xfff, 0xfff, 0xfff, 0x1ba, 0x06e, 0xfff, 0x1b1,
-	0xfff, 0xfff, 0x12a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x193, 0xfff, 0xfff, 0xfff, 0xfff, 0x10a,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x048, 0x1b4,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x295, 0x140, 0x108, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x16f, 0xfff, 0x0a4, 0x37a, 0xfff,
-	0x29a, 0xfff, 0x284, 0xfff, 0xfff, 0xfff, 0xfff, 0x4c6,
-	0x2a2, 0x3a3, 0xfff, 0x201, 0xfff, 0xfff, 0xfff, 0x4bd,
-	0x005, 0x54a, 0x3b5, 0x204, 0x2ee, 0x11d, 0x436, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x3ec, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x11f, 0x498, 0x21c, 0xfff,
-	0xfff, 0xfff, 0x3d6, 0xfff, 0x4ab, 0xfff, 0x432, 0x2eb,
-	0x542, 0x4fd, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x4ce, 0xfff, 0xfff, 0x2fb, 0xfff,
-	0xfff, 0x2e1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x1b9, 0x037, 0x0dd,
-	0xfff, 0xfff, 0xfff, 0x2bf, 0x521, 0x496, 0x095, 0xfff,
-	0xfff, 0x328, 0x070, 0x1bf, 0xfff, 0x393, 0xfff, 0xfff,
-	0x102, 0xfff, 0xfff, 0x21b, 0xfff, 0x142, 0x263, 0x519,
-	0xfff, 0x2a5, 0x177, 0xfff, 0x14d, 0x471, 0x4ae, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x1f6, 0xfff, 0x481, 0xfff, 0xfff, 0xfff, 0x151, 0xfff,
-	0xfff, 0xfff, 0x085, 0x33f, 0xfff, 0xfff, 0xfff, 0x084,
-	0xfff, 0xfff, 0xfff, 0x345, 0x3a2, 0xfff, 0xfff, 0x0a0,
-	0x0da, 0x024, 0xfff, 0xfff, 0xfff, 0x1bd, 0xfff, 0x55c,
-	0x467, 0x445, 0xfff, 0xfff, 0xfff, 0x052, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x51e, 0xfff, 0xfff, 0x39d, 0xfff, 0x35f,
-	0xfff, 0x376, 0x3ee, 0xfff, 0xfff, 0xfff, 0xfff, 0x448,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x16a,
-	0xfff, 0x036, 0x38f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x211,
-	0xfff, 0xfff, 0xfff, 0x230, 0xfff, 0xfff, 0x3ba, 0xfff,
-	0xfff, 0xfff, 0x3ce, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x229, 0xfff, 0x176, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x00b, 0xfff, 0x162, 0x018, 0xfff,
-	0xfff, 0x233, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x400, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x12b, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x3f4, 0xfff, 0x0f0, 0xfff, 0x1ac, 0xfff, 0xfff,
-	0x119, 0xfff, 0x2c0, 0xfff, 0xfff, 0xfff, 0x49b, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x23c, 0xfff,
-	0x4b3, 0x010, 0x064, 0xfff, 0xfff, 0x4ba, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x3c2, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x006, 0x196, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x100, 0x191, 0xfff,
-	0x1ea, 0x29f, 0xfff, 0xfff, 0xfff, 0x276, 0xfff, 0xfff,
-	0x2b1, 0x3b9, 0xfff, 0x03c, 0xfff, 0xfff, 0xfff, 0x180,
-	0xfff, 0x08f, 0xfff, 0xfff, 0x19e, 0x019, 0xfff, 0x0b0,
-	0x0fd, 0x332, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x06b, 0x2e8, 0xfff, 0x446, 0xfff, 0xfff, 0x004,
-	0x247, 0x197, 0xfff, 0x112, 0x169, 0x292, 0xfff, 0x302,
-	0xfff, 0xfff, 0x33b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x287, 0x21f, 0xfff, 0x3ea, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x4e7, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x3a8, 0xfff, 0xfff, 0x2bc, 0xfff,
-	0x484, 0x296, 0xfff, 0x1c9, 0x08c, 0x1e5, 0x48a, 0xfff,
-	0x360, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x1ca, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x10d, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x066, 0x2ea, 0x28b, 0x25b, 0xfff, 0x072,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x2b6, 0xfff, 0xfff, 0x272,
-	0xfff, 0xfff, 0x525, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x2ca, 0xfff, 0xfff, 0xfff, 0x299, 0xfff, 0xfff, 0xfff,
-	0x558, 0x41a, 0xfff, 0x4f7, 0x557, 0xfff, 0x4a0, 0x344,
-	0x12c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x125,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x40e, 0xfff, 0xfff, 0x502, 0xfff, 0x103, 0x3e6, 0xfff,
-	0x527, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x45d, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x44e, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d2, 0x4c9, 0x35e,
-	0x459, 0x2d9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x17d,
-	0x0c4, 0xfff, 0xfff, 0xfff, 0x3ac, 0x390, 0x094, 0xfff,
-	0x483, 0x0ab, 0xfff, 0x253, 0xfff, 0x391, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x123, 0x0ef, 0xfff, 0xfff, 0xfff, 0x330,
-	0x38c, 0xfff, 0xfff, 0x2ae, 0xfff, 0xfff, 0xfff, 0x042,
-	0x012, 0x06d, 0xfff, 0xfff, 0xfff, 0x32a, 0x3db, 0x364,
-	0x2dc, 0xfff, 0x30f, 0x3d7, 0x4a5, 0x050, 0xfff, 0xfff,
-	0x029, 0xfff, 0xfff, 0xfff, 0xfff, 0x1d1, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x480, 0xfff,
-	0x4ed, 0x081, 0x0a1, 0xfff, 0xfff, 0xfff, 0x30e, 0x52f,
-	0x257, 0xfff, 0xfff, 0x447, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x401, 0x3cc, 0xfff, 0xfff, 0x0fb,
-	0x2c9, 0x42a, 0x314, 0x33e, 0x3bd, 0x318, 0xfff, 0x10e,
-	0x2a1, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x24c,
-	0x506, 0xfff, 0x267, 0xfff, 0xfff, 0x219, 0xfff, 0x1eb,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x309, 0x3e2, 0x46c, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x384, 0xfff, 0xfff, 0xfff, 0xfff, 0x50c, 0xfff, 0x24b,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x038,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x194,
-	0x143, 0x3e3, 0xfff, 0xfff, 0xfff, 0x4c2, 0xfff, 0xfff,
-	0x0e1, 0x25c, 0xfff, 0x237, 0xfff, 0x1fe, 0xfff, 0xfff,
-	0xfff, 0x065, 0x2a4, 0xfff, 0x386, 0x55a, 0x11b, 0xfff,
-	0xfff, 0x192, 0xfff, 0x183, 0x00e, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x4b2, 0x18e, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x486, 0x4ef, 0x0c6, 0x380, 0xfff, 0x4a8, 0xfff,
-	0x0c5, 0xfff, 0xfff, 0xfff, 0xfff, 0x093, 0x1b8, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e6,
-	0xfff, 0x0f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x28e, 0xfff, 0x53b, 0x420, 0x22a, 0x33a, 0xfff, 0x387,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2a3, 0xfff, 0xfff,
-	0xfff, 0x428, 0x500, 0xfff, 0xfff, 0x120, 0x2c6, 0x290,
-	0x2f5, 0x0e3, 0xfff, 0x0b7, 0xfff, 0x319, 0x474, 0xfff,
-	0xfff, 0xfff, 0x529, 0x014, 0xfff, 0x41b, 0x40a, 0x18b,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x0d9,
-	0xfff, 0x38a, 0xfff, 0xfff, 0xfff, 0xfff, 0x1ce, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x3b1, 0xfff, 0xfff, 0x05d,
-	0x2c4, 0xfff, 0xfff, 0x4af, 0xfff, 0x030, 0xfff, 0xfff,
-	0x203, 0xfff, 0x277, 0x256, 0xfff, 0xfff, 0xfff, 0x4f9,
-	0xfff, 0x2c7, 0xfff, 0x466, 0x016, 0x1cd, 0xfff, 0x167,
-	0xfff, 0xfff, 0x0c8, 0xfff, 0x43d, 0xfff, 0xfff, 0x020,
-	0xfff, 0xfff, 0x232, 0x1cb, 0x1e0, 0xfff, 0xfff, 0x347,
-	0xfff, 0x478, 0xfff, 0x365, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x358, 0xfff, 0x10b, 0xfff, 0x35d, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x452, 0x22d, 0xfff, 0xfff, 0x47d, 0xfff,
-	0x2f3, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x460, 0xfff,
-	0xfff, 0xfff, 0x50b, 0xfff, 0xfff, 0xfff, 0x2ec, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x4b1, 0x422, 0xfff, 0xfff,
-	0xfff, 0x2d4, 0xfff, 0x239, 0xfff, 0xfff, 0xfff, 0x439,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x491, 0x075, 0xfff, 0xfff, 0xfff, 0x06c, 0xfff,
-	0xfff, 0x0f9, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x139, 0xfff, 0x4f6, 0xfff, 0xfff, 0x409, 0xfff,
-	0xfff, 0x15b, 0xfff, 0xfff, 0x348, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x4a2, 0x49d, 0xfff, 0x033, 0x175, 0xfff, 0x039,
-	0xfff, 0x312, 0x40c, 0xfff, 0xfff, 0x325, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x4aa, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0x165, 0x3bc, 0x48c, 0x310, 0x096,
-	0xfff, 0xfff, 0x250, 0x1a2, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x20d, 0x2ac, 0xfff, 0xfff, 0x39b, 0xfff, 0x377, 0xfff,
-	0x512, 0x495, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x357, 0x4ea, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x198, 0xfff, 0xfff, 0xfff, 0x434, 0x04a,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x062, 0xfff, 0x1d6, 0x1c8,
-	0xfff, 0x1f3, 0x281, 0xfff, 0x462, 0xfff, 0xfff, 0xfff,
-	0x4b0, 0xfff, 0x207, 0xfff, 0xfff, 0xfff, 0xfff, 0x3dd,
-	0xfff, 0xfff, 0x55d, 0xfff, 0x552, 0x494, 0x1af, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x227, 0xfff, 0xfff, 0x069,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x43e,
-	0x0b5, 0xfff, 0x524, 0x2d2, 0xfff, 0xfff, 0xfff, 0x28f,
-	0xfff, 0x01b, 0x50e, 0xfff, 0xfff, 0x1bb, 0xfff, 0xfff,
-	0x41d, 0xfff, 0x32e, 0x48e, 0xfff, 0x1f7, 0x224, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x394, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x52c, 0xfff, 0xfff, 0xfff, 0x392, 0xfff, 0x1e7,
-	0xfff, 0xfff, 0x3f9, 0x3a7, 0xfff, 0x51f, 0xfff, 0x0bb,
-	0x118, 0x3ca, 0xfff, 0x1dd, 0xfff, 0x48b, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x50f, 0xfff, 0x0d6, 0xfff, 0x1fa, 0xfff,
-	0x11e, 0xfff, 0xfff, 0xfff, 0xfff, 0x4d7, 0xfff, 0x078,
-	0x008, 0xfff, 0x25d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x032, 0x33c, 0xfff, 0x4d9, 0x160, 0xfff, 0xfff, 0x300,
-	0x0b1, 0xfff, 0x322, 0xfff, 0x4ec, 0xfff, 0xfff, 0x200,
-	0x00c, 0x369, 0x473, 0xfff, 0xfff, 0x32c, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x53e, 0x3d4, 0x417, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x34b, 0x001, 0x39a, 0x02c, 0xfff, 0xfff, 0x2ce, 0x00f,
-	0xfff, 0x0ba, 0xfff, 0xfff, 0xfff, 0xfff, 0x060, 0xfff,
-	0x406, 0xfff, 0xfff, 0xfff, 0x4ee, 0x4ac, 0xfff, 0x43f,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x29b, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x216,
-	0x190, 0xfff, 0x396, 0x464, 0xfff, 0xfff, 0x323, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x2e9, 0xfff, 0x26d,
-	0x2cd, 0x040, 0xfff, 0xfff, 0xfff, 0xfff, 0x38b, 0x3c0,
-	0xfff, 0xfff, 0xfff, 0x1f2, 0xfff, 0x0ea, 0xfff, 0xfff,
-	0x472, 0xfff, 0x1fb, 0xfff, 0xfff, 0x0af, 0x27f, 0xfff,
-	0xfff, 0xfff, 0x479, 0x023, 0xfff, 0x0d8, 0x3b3, 0xfff,
-	0xfff, 0xfff, 0x121, 0xfff, 0xfff, 0x3bf, 0xfff, 0xfff,
-	0x16b, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x45a, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x0be, 0xfff, 0xfff, 0xfff, 0x111, 0xfff, 0x220,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x09b, 0x218, 0xfff, 0x022, 0x202, 0xfff,
-	0x4c8, 0xfff, 0x0ed, 0xfff, 0xfff, 0x182, 0xfff, 0xfff,
-	0xfff, 0x17f, 0x213, 0xfff, 0x321, 0x36a, 0xfff, 0x086,
-	0xfff, 0xfff, 0xfff, 0x43b, 0x088, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x26c, 0xfff, 0x2f8, 0x3b4, 0xfff, 0xfff, 0xfff,
-	0x132, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x333, 0x444,
-	0x0c1, 0x4d8, 0x46d, 0x264, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x426, 0xfff, 0xfff, 0xfff, 0xfff, 0x2fe, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x011, 0xfff, 0x05f, 0xfff, 0xfff, 0xfff,
-	0xfff, 0x10c, 0x101, 0xfff, 0xfff, 0xfff, 0xfff, 0x110,
-	0xfff, 0x044, 0x304, 0x361, 0x404, 0xfff, 0x51b, 0x099,
-	0xfff, 0x440, 0xfff, 0xfff, 0xfff, 0x222, 0xfff, 0xfff,
-	0xfff, 0xfff, 0x1b5, 0xfff, 0x136, 0x430, 0xfff, 0x1da,
-	0xfff, 0xfff, 0xfff, 0x043, 0xfff, 0x17c, 0xfff, 0xfff,
-	0xfff, 0x01c, 0xfff, 0xfff, 0xfff, 0x425, 0x236, 0xfff,
-	0x317, 0xfff, 0xfff, 0x437, 0x3fc, 0xfff, 0x1f1, 0xfff,
-	0x324, 0xfff, 0xfff, 0x0ca, 0x306, 0xfff, 0x548, 0xfff,
-	0x46e, 0xfff, 0xfff, 0xfff, 0x4b8, 0x1c2, 0x286, 0xfff,
-	0xfff, 0x087, 0x18a, 0x19f, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x18c, 0xfff, 0x215, 0xfff, 0xfff, 0xfff, 0xfff, 0x283,
-	0xfff, 0xfff, 0xfff, 0x126, 0xfff, 0xfff, 0x370, 0xfff,
-	0x53f, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x31c, 0xfff,
-	0x4d1, 0xfff, 0xfff, 0xfff, 0x021, 0xfff, 0x157, 0xfff,
-	0xfff, 0x028, 0x16e, 0xfff, 0x421, 0xfff, 0x1c6, 0xfff,
-	0xfff, 0x511, 0xfff, 0xfff, 0x39c, 0x46f, 0x1b2, 0xfff,
-	0xfff, 0x316, 0xfff, 0xfff, 0x009, 0xfff, 0xfff, 0x195,
-	0xfff, 0x240, 0x546, 0xfff, 0xfff, 0x520, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x454, 0xfff, 0xfff, 0xfff,
-	0x3f3, 0xfff, 0xfff, 0x187, 0xfff, 0x4a9, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x51c, 0x453, 0x1e6, 0xfff,
-	0xfff, 0xfff, 0x1b0, 0xfff, 0x477, 0xfff, 0xfff, 0xfff,
-	0x4fe, 0xfff, 0x32f, 0xfff, 0xfff, 0x15e, 0x1d4, 0xfff,
-	0x0e5, 0xfff, 0xfff, 0xfff, 0x242, 0x14b, 0x046, 0xfff,
-	0x3f6, 0x3bb, 0x3e4, 0xfff, 0xfff, 0x2e3, 0xfff, 0x245,
-	0xfff, 0x149, 0xfff, 0xfff, 0xfff, 0x2db, 0xfff, 0xfff,
-	0x181, 0xfff, 0x089, 0x2c5, 0xfff, 0x1f5, 0xfff, 0x2d6,
-	0x507, 0xfff, 0x42d, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0x080, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
-	0xfff, 0xfff, 0xfff, 0xfff, 0x3c3, 0x320, 0xfff, 0x1e1,
-	0xfff, 0x0f5, 0x13b, 0xfff, 0xfff, 0xfff, 0x003, 0x4da,
-	0xfff, 0xfff, 0xfff, 0x42c, 0xfff, 0xfff, 0x0cb, 0xfff,
-	0x536, 0x2c3, 0xfff, 0xfff, 0xfff, 0xfff, 0x199, 0xfff,
-	0xfff, 0x0c0, 0xfff, 0x01e, 0x497, 0xfff, 0xfff, 0x3e5,
-	0xfff, 0xfff, 0xfff, 0x0cf, 0xfff, 0x2bd, 0xfff, 0x223,
-	0xfff, 0x3ff, 0xfff, 0x058, 0x174, 0x3ef, 0xfff, 0x002
-};
-
-static unsigned short err_pos(unsigned short din)
-{
-	BUG_ON(din >= ARRAY_SIZE(err_pos_lut));
-	return err_pos_lut[din];
-}
-static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
-{
-	if ((chk_syndrome_list[0] | chk_syndrome_list[1] |
-	     chk_syndrome_list[2] | chk_syndrome_list[3] |
-	     chk_syndrome_list[4] | chk_syndrome_list[5] |
-	     chk_syndrome_list[6] | chk_syndrome_list[7]) != 0x0) {
-		return -EINVAL;
-	} else {
-		err_info[0] = 0x0;
-		return 0;
-	}
-}
-static int chk_1_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
-{
-	unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
-	tmp0 = gf4096_mul(chk_syndrome_list[1], gf4096_inv(chk_syndrome_list[0]));
-	tmp1 = gf4096_mul(chk_syndrome_list[2], gf4096_inv(chk_syndrome_list[1]));
-	tmp2 = gf4096_mul(chk_syndrome_list[3], gf4096_inv(chk_syndrome_list[2]));
-	tmp3 = gf4096_mul(chk_syndrome_list[4], gf4096_inv(chk_syndrome_list[3]));
-	tmp4 = gf4096_mul(chk_syndrome_list[5], gf4096_inv(chk_syndrome_list[4]));
-	tmp5 = gf4096_mul(chk_syndrome_list[6], gf4096_inv(chk_syndrome_list[5]));
-	tmp6 = gf4096_mul(chk_syndrome_list[7], gf4096_inv(chk_syndrome_list[6]));
-	if ((tmp0 == tmp1) & (tmp1 == tmp2) & (tmp2 == tmp3) & (tmp3 == tmp4) & (tmp4 == tmp5) & (tmp5 == tmp6)) {
-		err_info[0] = 0x1;	// encode 1-symbol error as 0x1
-		err_info[1] = err_pos(tmp0);
-		err_info[1] = (unsigned short)(0x55e - err_info[1]);
-		err_info[5] = chk_syndrome_list[0];
-		return 0;
-	} else
-		return -EINVAL;
-}
-static int chk_2_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
-{
-	unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
-	unsigned short coefs[4];
-	unsigned short err_pats[4];
-	int found_num_root = 0;
-	unsigned short bit2_root0, bit2_root1;
-	unsigned short bit2_root0_inv, bit2_root1_inv;
-	unsigned short err_loc_eqn, test_root;
-	unsigned short bit2_loc0, bit2_loc1;
-	unsigned short bit2_pat0, bit2_pat1;
-
-	find_2x2_soln(chk_syndrome_list[1],
-		      chk_syndrome_list[0],
-		      chk_syndrome_list[2], chk_syndrome_list[1], chk_syndrome_list[2], chk_syndrome_list[3], coefs);
-	for (test_root = 0x1; test_root < 0xfff; test_root++) {
-		err_loc_eqn =
-		    gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) ^ gf4096_mul(coefs[0], test_root) ^ 0x1;
-		if (err_loc_eqn == 0x0) {
-			if (found_num_root == 0) {
-				bit2_root0 = test_root;
-				found_num_root = 1;
-			} else if (found_num_root == 1) {
-				bit2_root1 = test_root;
-				found_num_root = 2;
-				break;
-			}
-		}
-	}
-	if (found_num_root != 2)
-		return -EINVAL;
-	else {
-		bit2_root0_inv = gf4096_inv(bit2_root0);
-		bit2_root1_inv = gf4096_inv(bit2_root1);
-		find_2bit_err_pats(chk_syndrome_list[0],
-				   chk_syndrome_list[1], bit2_root0_inv, bit2_root1_inv, err_pats);
-		bit2_pat0 = err_pats[0];
-		bit2_pat1 = err_pats[1];
-		//for(x+1)
-		tmp0 = gf4096_mul(gf4096_mul(bit2_root0_inv, bit2_root0_inv), gf4096_mul(bit2_root0_inv, bit2_root0_inv));	//rinv0^4
-		tmp1 = gf4096_mul(bit2_root0_inv, tmp0);	//rinv0^5
-		tmp2 = gf4096_mul(bit2_root0_inv, tmp1);	//rinv0^6
-		tmp3 = gf4096_mul(bit2_root0_inv, tmp2);	//rinv0^7
-		tmp4 = gf4096_mul(gf4096_mul(bit2_root1_inv, bit2_root1_inv), gf4096_mul(bit2_root1_inv, bit2_root1_inv));	//rinv1^4
-		tmp5 = gf4096_mul(bit2_root1_inv, tmp4);	//rinv1^5
-		tmp6 = gf4096_mul(bit2_root1_inv, tmp5);	//rinv1^6
-		tmp7 = gf4096_mul(bit2_root1_inv, tmp6);	//rinv1^7
-		//check if only 2-bit error
-		if ((chk_syndrome_list[4] ==
-		     (gf4096_mul(bit2_pat0, tmp0) ^
-		      gf4096_mul(bit2_pat1,
-				 tmp4))) & (chk_syndrome_list[5] ==
-					    (gf4096_mul(bit2_pat0, tmp1) ^
-					     gf4096_mul(bit2_pat1,
-							tmp5))) &
-		    (chk_syndrome_list[6] ==
-		     (gf4096_mul(bit2_pat0, tmp2) ^
-		      gf4096_mul(bit2_pat1,
-				 tmp6))) & (chk_syndrome_list[7] ==
-					    (gf4096_mul(bit2_pat0, tmp3) ^ gf4096_mul(bit2_pat1, tmp7)))) {
-			if ((err_pos(bit2_root0_inv) == 0xfff) | (err_pos(bit2_root1_inv) == 0xfff)) {
-				return -EINVAL;
-			} else {
-				bit2_loc0 = 0x55e - err_pos(bit2_root0_inv);
-				bit2_loc1 = 0x55e - err_pos(bit2_root1_inv);
-				err_info[0] = 0x2;	// encode 2-symbol error as 0x2
-				err_info[1] = bit2_loc0;
-				err_info[2] = bit2_loc1;
-				err_info[5] = bit2_pat0;
-				err_info[6] = bit2_pat1;
-				return 0;
-			}
-		} else
-			return -EINVAL;
-	}
-}
-static int chk_3_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
-{
-	unsigned short tmp0, tmp1, tmp2, tmp3, tmp4, tmp5;
-	unsigned short coefs[4];
-	unsigned short err_pats[4];
-	int found_num_root = 0;
-	unsigned short bit3_root0, bit3_root1, bit3_root2;
-	unsigned short bit3_root0_inv, bit3_root1_inv, bit3_root2_inv;
-	unsigned short err_loc_eqn, test_root;
-
-	find_3bit_err_coefs(chk_syndrome_list[0], chk_syndrome_list[1],
-			    chk_syndrome_list[2], chk_syndrome_list[3],
-			    chk_syndrome_list[4], chk_syndrome_list[5], coefs);
-
-	for (test_root = 0x1; test_root < 0xfff; test_root++) {
-		err_loc_eqn = gf4096_mul(coefs[2],
-					 gf4096_mul(gf4096_mul(test_root, test_root),
-						    test_root)) ^ gf4096_mul(coefs[1], gf4096_mul(test_root, test_root))
-			^ gf4096_mul(coefs[0], test_root) ^ 0x1;
-
-		if (err_loc_eqn == 0x0) {
-			if (found_num_root == 0) {
-				bit3_root0 = test_root;
-				found_num_root = 1;
-			} else if (found_num_root == 1) {
-				bit3_root1 = test_root;
-				found_num_root = 2;
-			} else if (found_num_root == 2) {
-				bit3_root2 = test_root;
-				found_num_root = 3;
-				break;
-			}
-		}
-	}
-	if (found_num_root != 3)
-		return -EINVAL;
-	else {
-		bit3_root0_inv = gf4096_inv(bit3_root0);
-		bit3_root1_inv = gf4096_inv(bit3_root1);
-		bit3_root2_inv = gf4096_inv(bit3_root2);
-
-		find_3bit_err_pats(chk_syndrome_list[0], chk_syndrome_list[1],
-				   chk_syndrome_list[2], bit3_root0_inv,
-				   bit3_root1_inv, bit3_root2_inv, err_pats);
-
-		//check if only 3-bit error
-		tmp0 = gf4096_mul(bit3_root0_inv, bit3_root0_inv);
-		tmp0 = gf4096_mul(tmp0, tmp0);
-		tmp0 = gf4096_mul(tmp0, bit3_root0_inv);
-		tmp0 = gf4096_mul(tmp0, bit3_root0_inv);	//rinv0^6
-		tmp1 = gf4096_mul(tmp0, bit3_root0_inv);	//rinv0^7
-		tmp2 = gf4096_mul(bit3_root1_inv, bit3_root1_inv);
-		tmp2 = gf4096_mul(tmp2, tmp2);
-		tmp2 = gf4096_mul(tmp2, bit3_root1_inv);
-		tmp2 = gf4096_mul(tmp2, bit3_root1_inv);	//rinv1^6
-		tmp3 = gf4096_mul(tmp2, bit3_root1_inv);	//rinv1^7
-		tmp4 = gf4096_mul(bit3_root2_inv, bit3_root2_inv);
-		tmp4 = gf4096_mul(tmp4, tmp4);
-		tmp4 = gf4096_mul(tmp4, bit3_root2_inv);
-		tmp4 = gf4096_mul(tmp4, bit3_root2_inv);	//rinv2^6
-		tmp5 = gf4096_mul(tmp4, bit3_root2_inv);	//rinv2^7
-
-		//check if only 3 errors
-		if ((chk_syndrome_list[6] == (gf4096_mul(err_pats[0], tmp0) ^
-					      gf4096_mul(err_pats[1], tmp2) ^
-					      gf4096_mul(err_pats[2], tmp4))) &
-		    (chk_syndrome_list[7] == (gf4096_mul(err_pats[0], tmp1) ^
-					      gf4096_mul(err_pats[1], tmp3) ^ gf4096_mul(err_pats[2], tmp5)))) {
-			if ((err_pos(bit3_root0_inv) == 0xfff) |
-			    (err_pos(bit3_root1_inv) == 0xfff) | (err_pos(bit3_root2_inv) == 0xfff)) {
-				return -EINVAL;
-			} else {
-				err_info[0] = 0x3;
-				err_info[1] = (0x55e - err_pos(bit3_root0_inv));
-				err_info[2] = (0x55e - err_pos(bit3_root1_inv));
-				err_info[3] = (0x55e - err_pos(bit3_root2_inv));
-				err_info[5] = err_pats[0];
-				err_info[6] = err_pats[1];
-				err_info[7] = err_pats[2];
-				return 0;
-			}
-		} else
-			return -EINVAL;
-	}
-}
-static int chk_4_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
-{
-	unsigned short coefs[4];
-	unsigned short err_pats[4];
-	int found_num_root = 0;
-	unsigned short bit4_root0, bit4_root1, bit4_root2, bit4_root3;
-	unsigned short bit4_root0_inv, bit4_root1_inv, bit4_root2_inv, bit4_root3_inv;
-	unsigned short err_loc_eqn, test_root;
-
-	find_4bit_err_coefs(chk_syndrome_list[0],
-			    chk_syndrome_list[1],
-			    chk_syndrome_list[2],
-			    chk_syndrome_list[3],
-			    chk_syndrome_list[4],
-			    chk_syndrome_list[5], chk_syndrome_list[6], chk_syndrome_list[7], coefs);
-
-	for (test_root = 0x1; test_root < 0xfff; test_root++) {
-		err_loc_eqn =
-		    gf4096_mul(coefs[3],
-			       gf4096_mul(gf4096_mul
-					  (gf4096_mul(test_root, test_root),
-					   test_root),
-					  test_root)) ^ gf4096_mul(coefs[2],
-								   gf4096_mul
-								   (gf4096_mul(test_root, test_root), test_root))
-		    ^ gf4096_mul(coefs[1], gf4096_mul(test_root, test_root)) ^ gf4096_mul(coefs[0], test_root)
-		    ^ 0x1;
-		if (err_loc_eqn == 0x0) {
-			if (found_num_root == 0) {
-				bit4_root0 = test_root;
-				found_num_root = 1;
-			} else if (found_num_root == 1) {
-				bit4_root1 = test_root;
-				found_num_root = 2;
-			} else if (found_num_root == 2) {
-				bit4_root2 = test_root;
-				found_num_root = 3;
-			} else {
-				found_num_root = 4;
-				bit4_root3 = test_root;
-				break;
-			}
-		}
-	}
-	if (found_num_root != 4) {
-		return -EINVAL;
-	} else {
-		bit4_root0_inv = gf4096_inv(bit4_root0);
-		bit4_root1_inv = gf4096_inv(bit4_root1);
-		bit4_root2_inv = gf4096_inv(bit4_root2);
-		bit4_root3_inv = gf4096_inv(bit4_root3);
-		find_4bit_err_pats(chk_syndrome_list[0],
-				   chk_syndrome_list[1],
-				   chk_syndrome_list[2],
-				   chk_syndrome_list[3],
-				   bit4_root0_inv, bit4_root1_inv, bit4_root2_inv, bit4_root3_inv, err_pats);
-		err_info[0] = 0x4;
-		err_info[1] = (0x55e - err_pos(bit4_root0_inv));
-		err_info[2] = (0x55e - err_pos(bit4_root1_inv));
-		err_info[3] = (0x55e - err_pos(bit4_root2_inv));
-		err_info[4] = (0x55e - err_pos(bit4_root3_inv));
-		err_info[5] = err_pats[0];
-		err_info[6] = err_pats[1];
-		err_info[7] = err_pats[2];
-		err_info[8] = err_pats[3];
-		return 0;
-	}
-}
-
-void correct_12bit_symbol(unsigned char *buf, unsigned short sym,
-			  unsigned short val)
-{
-	if (unlikely(sym > 1366)) {
-		printk(KERN_ERR "Error: symbol %d out of range; cannot correct\n", sym);
-	} else if (sym == 0) {
-		buf[0] ^= val;
-	} else if (sym & 1) {
-		buf[1+(3*(sym-1))/2] ^= (val >> 4);
-		buf[2+(3*(sym-1))/2] ^= ((val & 0xf) << 4);
-	} else {
-		buf[2+(3*(sym-2))/2] ^= (val >> 8);
-		buf[3+(3*(sym-2))/2] ^= (val & 0xff);
-	}
-}
-
-static int debugecc = 0;
-module_param(debugecc, int, 0644);
-
-int cafe_correct_ecc(unsigned char *buf,
-		     unsigned short *chk_syndrome_list)
-{
-	unsigned short err_info[9];
-	int i;
-
-	if (debugecc) {
-		printk(KERN_WARNING "cafe_correct_ecc invoked. Syndromes %x %x %x %x %x %x %x %x\n",
-		       chk_syndrome_list[0], chk_syndrome_list[1],
-		       chk_syndrome_list[2], chk_syndrome_list[3],
-		       chk_syndrome_list[4], chk_syndrome_list[5],
-		       chk_syndrome_list[6], chk_syndrome_list[7]);
-		for (i=0; i < 2048; i+=16) {
-			printk(KERN_WARNING "D %04x: %02x %02x %02x %02x %02x %02x %02x %02x  %02x %02x %02x %02x %02x %02x %02x %02x\n",
-			       i,
-			       buf[i], buf[i+1], buf[i+2], buf[i+3],
-			       buf[i+4], buf[i+5], buf[i+6], buf[i+7],
-			       buf[i+8], buf[i+9], buf[i+10], buf[i+11],
-			       buf[i+12], buf[i+13], buf[i+14], buf[i+15]);
-		}
-		for ( ; i < 2112; i+=16) {
-			printk(KERN_WARNING "O   %02x: %02x %02x %02x %02x %02x %02x %02x %02x  %02x %02x %02x %02x %02x %02x %02x %02x\n",
-			       i - 2048,
-			       buf[i], buf[i+1], buf[i+2], buf[i+3],
-			       buf[i+4], buf[i+5], buf[i+6], buf[i+7],
-			       buf[i+8], buf[i+9], buf[i+10], buf[i+11],
-			       buf[i+12], buf[i+13], buf[i+14], buf[i+15]);
-		}
-	}
-
-
-
-	if (chk_no_err_only(chk_syndrome_list, err_info) &&
-	    chk_1_err_only(chk_syndrome_list, err_info) &&
-	    chk_2_err_only(chk_syndrome_list, err_info) &&
-	    chk_3_err_only(chk_syndrome_list, err_info) &&
-	    chk_4_err_only(chk_syndrome_list, err_info)) {
-		return -EIO;
-	}
-
-	for (i=0; i < err_info[0]; i++) {
-		if (debugecc)
-			printk(KERN_WARNING "Correct symbol %d with 0x%03x\n",
-			       err_info[1+i], err_info[5+i]);
-
-		correct_12bit_symbol(buf, err_info[1+i], err_info[5+i]);
-	}
-
-	return err_info[0];
-}
-


From linux-mtd at lists.infradead.org  Wed May  2 07:59:02 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 02 May 2007 12:59:02 +0100
Subject: =?UTF-8?Q?[MTD]_[NAND]_Support_multiple_chips_in_CAF=C3=89_driver?=
Message-ID: <E1HjDU6-00060b-Iy@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=048c37b4907ecf0e55100365e20758667f0bd27d
Commit:     048c37b4907ecf0e55100365e20758667f0bd27d
Parent:     14448005abd10887a2d361e20e04760dc3d8482f
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Wed May 2 12:26:37 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 2 12:26:37 2007 +0100

    [MTD] [NAND] Support multiple chips in CAF? driver
    
    The CAF? can handle two chip on separate chipselect lines. Hook up the
    undocumented chipselect bits in the driver and probe both.
    
    In the case of OLPC, it's not actually two separate devices -- it's a
    single '1GiB' package with two 512MiB dies internally. So clear the
    NAND_BBT_PERCHIP flag to treat it as a single chip for BBT purposes, and
    make life easier for the firmware.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/nand/cafe_nand.c |   29 ++++++++++++++++++++---------
 1 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index 05f6ec9..cff969d 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -47,6 +47,9 @@
 #define CAFE_GLOBAL_IRQ_MASK	0x300c
 #define CAFE_NAND_RESET		0x3034
 
+/* Missing from the datasheet: bit 19 of CTRL1 sets CE0 vs. CE1 */
+#define CTRL1_CHIPSELECT	(1<<19)
+
 struct cafe_priv {
 	struct nand_chip nand;
 	struct pci_dev *pdev;
@@ -194,8 +197,8 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 
 	cafe->data_pos = cafe->datalen = 0;
 
-	/* Set command valid bit */
-	ctl1 = 0x80000000 | command;
+	/* Set command valid bit, mask in the chip select bit  */
+	ctl1 = 0x80000000 | command | (cafe->ctl1 & CTRL1_CHIPSELECT);
 
 	/* Set RD or WR bits as appropriate */
 	if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) {
@@ -308,8 +311,16 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
 
 static void cafe_select_chip(struct mtd_info *mtd, int chipnr)
 {
-	//struct cafe_priv *cafe = mtd->priv;
-	//	cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr);
+	struct cafe_priv *cafe = mtd->priv;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr);
+
+	/* Mask the appropriate bit into the stored value of ctl1
+	   which will be used by cafe_nand_cmdfunc() */
+	if (chipnr)
+		cafe->ctl1 |= CTRL1_CHIPSELECT;
+	else
+		cafe->ctl1 &= ~CTRL1_CHIPSELECT;
 }
 
 static int cafe_nand_interrupt(int irq, void *id)
@@ -453,7 +464,7 @@ static uint8_t cafe_mirror_pattern_512[] = { 0xBC };
 
 static struct nand_bbt_descr cafe_bbt_main_descr_2048 = {
 	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+		| NAND_BBT_2BIT | NAND_BBT_VERSION,
 	.offs =	14,
 	.len = 4,
 	.veroffs = 18,
@@ -463,7 +474,7 @@ static struct nand_bbt_descr cafe_bbt_main_descr_2048 = {
 
 static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
 	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+		| NAND_BBT_2BIT | NAND_BBT_VERSION,
 	.offs =	14,
 	.len = 4,
 	.veroffs = 18,
@@ -479,7 +490,7 @@ static struct nand_ecclayout cafe_oobinfo_512 = {
 
 static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
 	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+		| NAND_BBT_2BIT | NAND_BBT_VERSION,
 	.offs =	14,
 	.len = 1,
 	.veroffs = 15,
@@ -489,7 +500,7 @@ static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
 
 static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
 	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+		| NAND_BBT_2BIT | NAND_BBT_VERSION,
 	.offs =	14,
 	.len = 1,
 	.veroffs = 15,
@@ -731,7 +742,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
 		cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
 
 	/* Scan to find existence of the device */
-	if (nand_scan_ident(mtd, 1)) {
+	if (nand_scan_ident(mtd, 2)) {
 		err = -ENXIO;
 		goto out_irq;
 	}


From linux-mtd at lists.infradead.org  Wed May  2 07:59:02 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 02 May 2007 12:59:02 +0100
Subject: [MTD] [NAND] Rename cafe.c to cafe_nand.c and remove the multi-obj
	magic
Message-ID: <E1HjDU6-000609-8Q@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=14448005abd10887a2d361e20e04760dc3d8482f
Commit:     14448005abd10887a2d361e20e04760dc3d8482f
Parent:     8c61b7a7f4d4e46be61cf1777361749b2d300c14
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Wed May 2 12:04:57 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 2 12:04:57 2007 +0100

    [MTD] [NAND] Rename cafe.c to cafe_nand.c and remove the multi-obj magic
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/nand/Makefile    |    1 -
 drivers/mtd/nand/cafe.c      |  838 ------------------------------------------
 drivers/mtd/nand/cafe_nand.c |  838 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 838 insertions(+), 839 deletions(-)

diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 79e9e5d..de1424b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -28,4 +28,3 @@ obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)	+= excite_nandflash.o
 
 nand-objs := nand_base.o nand_bbt.o
-cafe_nand-objs := cafe.o
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c
deleted file mode 100644
index 05f6ec9..0000000
--- a/drivers/mtd/nand/cafe.c
+++ /dev/null
@@ -1,838 +0,0 @@
-/*
- * Driver for One Laptop Per Child ?CAF?? controller, aka Marvell 88ALP01
- *
- * Copyright ? 2006 Red Hat, Inc.
- * Copyright ? 2006 David Woodhouse <dwmw2 at infradead.org>
- */
-
-#define DEBUG
-
-#include <linux/device.h>
-#undef DEBUG
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/rslib.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <asm/io.h>
-
-#define CAFE_NAND_CTRL1		0x00
-#define CAFE_NAND_CTRL2		0x04
-#define CAFE_NAND_CTRL3		0x08
-#define CAFE_NAND_STATUS	0x0c
-#define CAFE_NAND_IRQ		0x10
-#define CAFE_NAND_IRQ_MASK	0x14
-#define CAFE_NAND_DATA_LEN	0x18
-#define CAFE_NAND_ADDR1		0x1c
-#define CAFE_NAND_ADDR2		0x20
-#define CAFE_NAND_TIMING1	0x24
-#define CAFE_NAND_TIMING2	0x28
-#define CAFE_NAND_TIMING3	0x2c
-#define CAFE_NAND_NONMEM	0x30
-#define CAFE_NAND_ECC_RESULT	0x3C
-#define CAFE_NAND_DMA_CTRL	0x40
-#define CAFE_NAND_DMA_ADDR0	0x44
-#define CAFE_NAND_DMA_ADDR1	0x48
-#define CAFE_NAND_ECC_SYN01	0x50
-#define CAFE_NAND_ECC_SYN23	0x54
-#define CAFE_NAND_ECC_SYN45	0x58
-#define CAFE_NAND_ECC_SYN67	0x5c
-#define CAFE_NAND_READ_DATA	0x1000
-#define CAFE_NAND_WRITE_DATA	0x2000
-
-#define CAFE_GLOBAL_CTRL	0x3004
-#define CAFE_GLOBAL_IRQ		0x3008
-#define CAFE_GLOBAL_IRQ_MASK	0x300c
-#define CAFE_NAND_RESET		0x3034
-
-struct cafe_priv {
-	struct nand_chip nand;
-	struct pci_dev *pdev;
-	void __iomem *mmio;
-	struct rs_control *rs;
-	uint32_t ctl1;
-	uint32_t ctl2;
-	int datalen;
-	int nr_data;
-	int data_pos;
-	int page_addr;
-	dma_addr_t dmaaddr;
-	unsigned char *dmabuf;
-};
-
-static int usedma = 1;
-module_param(usedma, int, 0644);
-
-static int skipbbt = 0;
-module_param(skipbbt, int, 0644);
-
-static int debug = 0;
-module_param(debug, int, 0644);
-
-static int regdebug = 0;
-module_param(regdebug, int, 0644);
-
-static int checkecc = 1;
-module_param(checkecc, int, 0644);
-
-static int numtimings;
-static int timing[3];
-module_param_array(timing, int, &numtimings, 0644);
-
-/* Hrm. Why isn't this already conditional on something in the struct device? */
-#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
-
-/* Make it easier to switch to PIO if we need to */
-#define cafe_readl(cafe, addr)			readl((cafe)->mmio + CAFE_##addr)
-#define cafe_writel(cafe, datum, addr)		writel(datum, (cafe)->mmio + CAFE_##addr)
-
-static int cafe_device_ready(struct mtd_info *mtd)
-{
-	struct cafe_priv *cafe = mtd->priv;
-	int result = !!(cafe_readl(cafe, NAND_STATUS) | 0x40000000);
-	uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
-
-	cafe_writel(cafe, irqs, NAND_IRQ);
-
-	cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n",
-		result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ),
-		cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK));
-
-	return result;
-}
-
-
-static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
-{
-	struct cafe_priv *cafe = mtd->priv;
-
-	if (usedma)
-		memcpy(cafe->dmabuf + cafe->datalen, buf, len);
-	else
-		memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len);
-
-	cafe->datalen += len;
-
-	cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n",
-		len, cafe->datalen);
-}
-
-static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
-{
-	struct cafe_priv *cafe = mtd->priv;
-
-	if (usedma)
-		memcpy(buf, cafe->dmabuf + cafe->datalen, len);
-	else
-		memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len);
-
-	cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n",
-		  len, cafe->datalen);
-	cafe->datalen += len;
-}
-
-static uint8_t cafe_read_byte(struct mtd_info *mtd)
-{
-	struct cafe_priv *cafe = mtd->priv;
-	uint8_t d;
-
-	cafe_read_buf(mtd, &d, 1);
-	cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d);
-
-	return d;
-}
-
-static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
-			      int column, int page_addr)
-{
-	struct cafe_priv *cafe = mtd->priv;
-	int adrbytes = 0;
-	uint32_t ctl1;
-	uint32_t doneint = 0x80000000;
-
-	cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n",
-		command, column, page_addr);
-
-	if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) {
-		/* Second half of a command we already calculated */
-		cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2);
-		ctl1 = cafe->ctl1;
-		cafe->ctl2 &= ~(1<<30);
-		cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n",
-			  cafe->ctl1, cafe->nr_data);
-		goto do_command;
-	}
-	/* Reset ECC engine */
-	cafe_writel(cafe, 0, NAND_CTRL2);
-
-	/* Emulate NAND_CMD_READOOB on large-page chips */
-	if (mtd->writesize > 512 &&
-	    command == NAND_CMD_READOOB) {
-		column += mtd->writesize;
-		command = NAND_CMD_READ0;
-	}
-
-	/* FIXME: Do we need to send read command before sending data
-	   for small-page chips, to position the buffer correctly? */
-
-	if (column != -1) {
-		cafe_writel(cafe, column, NAND_ADDR1);
-		adrbytes = 2;
-		if (page_addr != -1)
-			goto write_adr2;
-	} else if (page_addr != -1) {
-		cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1);
-		page_addr >>= 16;
-	write_adr2:
-		cafe_writel(cafe, page_addr, NAND_ADDR2);
-		adrbytes += 2;
-		if (mtd->size > mtd->writesize << 16)
-			adrbytes++;
-	}
-
-	cafe->data_pos = cafe->datalen = 0;
-
-	/* Set command valid bit */
-	ctl1 = 0x80000000 | command;
-
-	/* Set RD or WR bits as appropriate */
-	if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) {
-		ctl1 |= (1<<26); /* rd */
-		/* Always 5 bytes, for now */
-		cafe->datalen = 4;
-		/* And one address cycle -- even for STATUS, since the controller doesn't work without */
-		adrbytes = 1;
-	} else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
-		   command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) {
-		ctl1 |= 1<<26; /* rd */
-		/* For now, assume just read to end of page */
-		cafe->datalen = mtd->writesize + mtd->oobsize - column;
-	} else if (command == NAND_CMD_SEQIN)
-		ctl1 |= 1<<25; /* wr */
-
-	/* Set number of address bytes */
-	if (adrbytes)
-		ctl1 |= ((adrbytes-1)|8) << 27;
-
-	if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) {
-		/* Ignore the first command of a pair; the hardware
-		   deals with them both at once, later */
-		cafe->ctl1 = ctl1;
-		cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n",
-			  cafe->ctl1, cafe->datalen);
-		return;
-	}
-	/* RNDOUT and READ0 commands need a following byte */
-	if (command == NAND_CMD_RNDOUT)
-		cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2);
-	else if (command == NAND_CMD_READ0 && mtd->writesize > 512)
-		cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2);
-
- do_command:
-	cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n",
-		cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2));
-
-	/* NB: The datasheet lies -- we really should be subtracting 1 here */
-	cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN);
-	cafe_writel(cafe, 0x90000000, NAND_IRQ);
-	if (usedma && (ctl1 & (3<<25))) {
-		uint32_t dmactl = 0xc0000000 + cafe->datalen;
-		/* If WR or RD bits set, set up DMA */
-		if (ctl1 & (1<<26)) {
-			/* It's a read */
-			dmactl |= (1<<29);
-			/* ... so it's done when the DMA is done, not just
-			   the command. */
-			doneint = 0x10000000;
-		}
-		cafe_writel(cafe, dmactl, NAND_DMA_CTRL);
-	}
-	cafe->datalen = 0;
-
-	if (unlikely(regdebug)) {
-		int i;
-		printk("About to write command %08x to register 0\n", ctl1);
-		for (i=4; i< 0x5c; i+=4)
-			printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
-	}
-
-	cafe_writel(cafe, ctl1, NAND_CTRL1);
-	/* Apply this short delay always to ensure that we do wait tWB in
-	 * any case on any machine. */
-	ndelay(100);
-
-	if (1) {
-		int c;
-		uint32_t irqs;
-
-		for (c = 500000; c != 0; c--) {
-			irqs = cafe_readl(cafe, NAND_IRQ);
-			if (irqs & doneint)
-				break;
-			udelay(1);
-			if (!(c % 100000))
-				cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs);
-			cpu_relax();
-		}
-		cafe_writel(cafe, doneint, NAND_IRQ);
-		cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n",
-			     command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ));
-	}
-
-	WARN_ON(cafe->ctl2 & (1<<30));
-
-	switch (command) {
-
-	case NAND_CMD_CACHEDPROG:
-	case NAND_CMD_PAGEPROG:
-	case NAND_CMD_ERASE1:
-	case NAND_CMD_ERASE2:
-	case NAND_CMD_SEQIN:
-	case NAND_CMD_RNDIN:
-	case NAND_CMD_STATUS:
-	case NAND_CMD_DEPLETE1:
-	case NAND_CMD_RNDOUT:
-	case NAND_CMD_STATUS_ERROR:
-	case NAND_CMD_STATUS_ERROR0:
-	case NAND_CMD_STATUS_ERROR1:
-	case NAND_CMD_STATUS_ERROR2:
-	case NAND_CMD_STATUS_ERROR3:
-		cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
-		return;
-	}
-	nand_wait_ready(mtd);
-	cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
-}
-
-static void cafe_select_chip(struct mtd_info *mtd, int chipnr)
-{
-	//struct cafe_priv *cafe = mtd->priv;
-	//	cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr);
-}
-
-static int cafe_nand_interrupt(int irq, void *id)
-{
-	struct mtd_info *mtd = id;
-	struct cafe_priv *cafe = mtd->priv;
-	uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
-	cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ);
-	if (!irqs)
-		return IRQ_NONE;
-
-	cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ));
-	return IRQ_HANDLED;
-}
-
-static void cafe_nand_bug(struct mtd_info *mtd)
-{
-	BUG();
-}
-
-static int cafe_nand_write_oob(struct mtd_info *mtd,
-			       struct nand_chip *chip, int page)
-{
-	int status = 0;
-
-	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
-	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-	status = chip->waitfunc(mtd, chip);
-
-	return status & NAND_STATUS_FAIL ? -EIO : 0;
-}
-
-/* Don't use -- use nand_read_oob_std for now */
-static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
-			      int page, int sndcmd)
-{
-	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
-	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-	return 1;
-}
-/**
- * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
- * @mtd:	mtd info structure
- * @chip:	nand chip info structure
- * @buf:	buffer to store read data
- *
- * The hw generator calculates the error syndrome automatically. Therefor
- * we need a special oob layout and handling.
- */
-static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
-			       uint8_t *buf)
-{
-	struct cafe_priv *cafe = mtd->priv;
-
-	cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n",
-		     cafe_readl(cafe, NAND_ECC_RESULT),
-		     cafe_readl(cafe, NAND_ECC_SYN01));
-
-	chip->read_buf(mtd, buf, mtd->writesize);
-	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
-	if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
-		unsigned short syn[8], pat[4];
-		int pos[4];
-		u8 *oob = chip->oob_poi;
-		int i, n;
-
-		for (i=0; i<8; i+=2) {
-			uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
-			syn[i] = cafe->rs->index_of[tmp & 0xfff];
-			syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff];
-		}
-
-		n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0,
-		                pat);
-
-		for (i = 0; i < n; i++) {
-			int p = pos[i];
-
-			/* The 12-bit symbols are mapped to bytes here */
-
-			if (p > 1374) {
-				/* out of range */
-				n = -1374;
-			} else if (p == 0) {
-				/* high four bits do not correspond to data */
-				if (pat[i] > 0xff)
-					n = -2048;
-				else
-					buf[0] ^= pat[i];
-			} else if (p == 1365) {
-				buf[2047] ^= pat[i] >> 4;
-				oob[0] ^= pat[i] << 4;
-			} else if (p > 1365) {
-				if ((p & 1) == 1) {
-					oob[3*p/2 - 2048] ^= pat[i] >> 4;
-					oob[3*p/2 - 2047] ^= pat[i] << 4;
-				} else {
-					oob[3*p/2 - 2049] ^= pat[i] >> 8;
-					oob[3*p/2 - 2048] ^= pat[i];
-				}
-			} else if ((p & 1) == 1) {
-				buf[3*p/2] ^= pat[i] >> 4;
-				buf[3*p/2 + 1] ^= pat[i] << 4;
-			} else {
-				buf[3*p/2 - 1] ^= pat[i] >> 8;
-				buf[3*p/2] ^= pat[i];
-			}
-		}
-
-		if (n < 0) {
-			dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
-				cafe_readl(cafe, NAND_ADDR2) * 2048);
-			for (i = 0; i < 0x5c; i += 4)
-				printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
-			mtd->ecc_stats.failed++;
-		} else {
-			dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n);
-			mtd->ecc_stats.corrected += n;
-		}
-	}
-
-	return 0;
-}
-
-static struct nand_ecclayout cafe_oobinfo_2048 = {
-	.eccbytes = 14,
-	.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
-	.oobfree = {{14, 50}}
-};
-
-/* Ick. The BBT code really ought to be able to work this bit out
-   for itself from the above, at least for the 2KiB case */
-static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' };
-static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' };
-
-static uint8_t cafe_bbt_pattern_512[] = { 0xBB };
-static uint8_t cafe_mirror_pattern_512[] = { 0xBC };
-
-
-static struct nand_bbt_descr cafe_bbt_main_descr_2048 = {
-	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
-	.offs =	14,
-	.len = 4,
-	.veroffs = 18,
-	.maxblocks = 4,
-	.pattern = cafe_bbt_pattern_2048
-};
-
-static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
-	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
-	.offs =	14,
-	.len = 4,
-	.veroffs = 18,
-	.maxblocks = 4,
-	.pattern = cafe_mirror_pattern_2048
-};
-
-static struct nand_ecclayout cafe_oobinfo_512 = {
-	.eccbytes = 14,
-	.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
-	.oobfree = {{14, 2}}
-};
-
-static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
-	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
-	.offs =	14,
-	.len = 1,
-	.veroffs = 15,
-	.maxblocks = 4,
-	.pattern = cafe_bbt_pattern_512
-};
-
-static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
-	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
-	.offs =	14,
-	.len = 1,
-	.veroffs = 15,
-	.maxblocks = 4,
-	.pattern = cafe_mirror_pattern_512
-};
-
-
-static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
-					  struct nand_chip *chip, const uint8_t *buf)
-{
-	struct cafe_priv *cafe = mtd->priv;
-
-	chip->write_buf(mtd, buf, mtd->writesize);
-	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
-	/* Set up ECC autogeneration */
-	cafe->ctl2 |= (1<<30);
-}
-
-static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
-				const uint8_t *buf, int page, int cached, int raw)
-{
-	int status;
-
-	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-
-	if (unlikely(raw))
-		chip->ecc.write_page_raw(mtd, chip, buf);
-	else
-		chip->ecc.write_page(mtd, chip, buf);
-
-	/*
-	 * Cached progamming disabled for now, Not sure if its worth the
-	 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
-	 */
-	cached = 0;
-
-	if (!cached || !(chip->options & NAND_CACHEPRG)) {
-
-		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-		status = chip->waitfunc(mtd, chip);
-		/*
-		 * See if operation failed and additional status checks are
-		 * available
-		 */
-		if ((status & NAND_STATUS_FAIL) && (chip->errstat))
-			status = chip->errstat(mtd, chip, FL_WRITING, status,
-					       page);
-
-		if (status & NAND_STATUS_FAIL)
-			return -EIO;
-	} else {
-		chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
-		status = chip->waitfunc(mtd, chip);
-	}
-
-#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
-	/* Send command to read back the data */
-	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-
-	if (chip->verify_buf(mtd, buf, mtd->writesize))
-		return -EIO;
-#endif
-	return 0;
-}
-
-static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
-{
-	return 0;
-}
-
-/* F_2[X]/(X**6+X+1)  */
-static unsigned short __devinit gf64_mul(u8 a, u8 b)
-{
-	u8 c;
-	unsigned int i;
-
-	c = 0;
-	for (i = 0; i < 6; i++) {
-		if (a & 1)
-			c ^= b;
-		a >>= 1;
-		b <<= 1;
-		if ((b & 0x40) != 0)
-			b ^= 0x43;
-	}
-
-	return c;
-}
-
-/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X]  */
-static u16 __devinit gf4096_mul(u16 a, u16 b)
-{
-	u8 ah, al, bh, bl, ch, cl;
-
-	ah = a >> 6;
-	al = a & 0x3f;
-	bh = b >> 6;
-	bl = b & 0x3f;
-
-	ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl);
-	cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl);
-
-	return (ch << 6) ^ cl;
-}
-
-static int __devinit cafe_mul(int x)
-{
-	if (x == 0)
-		return 1;
-	return gf4096_mul(x, 0xe01);
-}
-
-static int __devinit cafe_nand_probe(struct pci_dev *pdev,
-				     const struct pci_device_id *ent)
-{
-	struct mtd_info *mtd;
-	struct cafe_priv *cafe;
-	uint32_t ctrl;
-	int err = 0;
-
-	err = pci_enable_device(pdev);
-	if (err)
-		return err;
-
-	pci_set_master(pdev);
-
-	mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL);
-	if (!mtd) {
-		dev_warn(&pdev->dev, "failed to alloc mtd_info\n");
-		return  -ENOMEM;
-	}
-	cafe = (void *)(&mtd[1]);
-
-	mtd->priv = cafe;
-	mtd->owner = THIS_MODULE;
-
-	cafe->pdev = pdev;
-	cafe->mmio = pci_iomap(pdev, 0, 0);
-	if (!cafe->mmio) {
-		dev_warn(&pdev->dev, "failed to iomap\n");
-		err = -ENOMEM;
-		goto out_free_mtd;
-	}
-	cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers),
-					  &cafe->dmaaddr, GFP_KERNEL);
-	if (!cafe->dmabuf) {
-		err = -ENOMEM;
-		goto out_ior;
-	}
-	cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
-
-	cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
-	if (!cafe->rs) {
-		err = -ENOMEM;
-		goto out_ior;
-	}
-
-	cafe->nand.cmdfunc = cafe_nand_cmdfunc;
-	cafe->nand.dev_ready = cafe_device_ready;
-	cafe->nand.read_byte = cafe_read_byte;
-	cafe->nand.read_buf = cafe_read_buf;
-	cafe->nand.write_buf = cafe_write_buf;
-	cafe->nand.select_chip = cafe_select_chip;
-
-	cafe->nand.chip_delay = 0;
-
-	/* Enable the following for a flash based bad block table */
-	cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS;
-
-	if (skipbbt) {
-		cafe->nand.options |= NAND_SKIP_BBTSCAN;
-		cafe->nand.block_bad = cafe_nand_block_bad;
-	}
-
-	if (numtimings && numtimings != 3) {
-		dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings);
-	}
-
-	if (numtimings == 3) {
-		cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
-			     timing[0], timing[1], timing[2]);
-	} else {
-		timing[0] = cafe_readl(cafe, NAND_TIMING1);
-		timing[1] = cafe_readl(cafe, NAND_TIMING2);
-		timing[2] = cafe_readl(cafe, NAND_TIMING3);
-
-		if (timing[0] | timing[1] | timing[2]) {
-			cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n",
-				     timing[0], timing[1], timing[2]);
-		} else {
-			dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
-			timing[0] = timing[1] = timing[2] = 0xffffffff;
-		}
-	}
-
-	/* Start off by resetting the NAND controller completely */
-	cafe_writel(cafe, 1, NAND_RESET);
-	cafe_writel(cafe, 0, NAND_RESET);
-
-	cafe_writel(cafe, timing[0], NAND_TIMING1);
-	cafe_writel(cafe, timing[1], NAND_TIMING2);
-	cafe_writel(cafe, timing[2], NAND_TIMING3);
-
-	cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
-	err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
-			  "CAFE NAND", mtd);
-	if (err) {
-		dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
-		goto out_free_dma;
-	}
-
-	/* Disable master reset, enable NAND clock */
-	ctrl = cafe_readl(cafe, GLOBAL_CTRL);
-	ctrl &= 0xffffeff0;
-	ctrl |= 0x00007000;
-	cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
-	cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
-	cafe_writel(cafe, 0, NAND_DMA_CTRL);
-
-	cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
-	cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
-
-	/* Set up DMA address */
-	cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
-	if (sizeof(cafe->dmaaddr) > 4)
-		/* Shift in two parts to shut the compiler up */
-		cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1);
-	else
-		cafe_writel(cafe, 0, NAND_DMA_ADDR1);
-
-	cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
-		cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
-
-	/* Enable NAND IRQ in global IRQ mask register */
-	cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
-	cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
-		cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
-
-	/* Scan to find existence of the device */
-	if (nand_scan_ident(mtd, 1)) {
-		err = -ENXIO;
-		goto out_irq;
-	}
-
-	cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
-	if (mtd->writesize == 2048)
-		cafe->ctl2 |= 1<<29; /* 2KiB page size */
-
-	/* Set up ECC according to the type of chip we found */
-	if (mtd->writesize == 2048) {
-		cafe->nand.ecc.layout = &cafe_oobinfo_2048;
-		cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
-		cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
-	} else if (mtd->writesize == 512) {
-		cafe->nand.ecc.layout = &cafe_oobinfo_512;
-		cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
-		cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
-	} else {
-		printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
-		       mtd->writesize);
-		goto out_irq;
-	}
-	cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
-	cafe->nand.ecc.size = mtd->writesize;
-	cafe->nand.ecc.bytes = 14;
-	cafe->nand.ecc.hwctl  = (void *)cafe_nand_bug;
-	cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
-	cafe->nand.ecc.correct  = (void *)cafe_nand_bug;
-	cafe->nand.write_page = cafe_nand_write_page;
-	cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
-	cafe->nand.ecc.write_oob = cafe_nand_write_oob;
-	cafe->nand.ecc.read_page = cafe_nand_read_page;
-	cafe->nand.ecc.read_oob = cafe_nand_read_oob;
-
-	err = nand_scan_tail(mtd);
-	if (err)
-		goto out_irq;
-
-	pci_set_drvdata(pdev, mtd);
-	add_mtd_device(mtd);
-	goto out;
-
- out_irq:
-	/* Disable NAND IRQ in global IRQ mask register */
-	cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
-	free_irq(pdev->irq, mtd);
- out_free_dma:
-	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
- out_ior:
-	pci_iounmap(pdev, cafe->mmio);
- out_free_mtd:
-	kfree(mtd);
- out:
-	return err;
-}
-
-static void __devexit cafe_nand_remove(struct pci_dev *pdev)
-{
-	struct mtd_info *mtd = pci_get_drvdata(pdev);
-	struct cafe_priv *cafe = mtd->priv;
-
-	del_mtd_device(mtd);
-	/* Disable NAND IRQ in global IRQ mask register */
-	cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
-	free_irq(pdev->irq, mtd);
-	nand_release(mtd);
-	free_rs(cafe->rs);
-	pci_iounmap(pdev, cafe->mmio);
-	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
-	kfree(mtd);
-}
-
-static struct pci_device_id cafe_nand_tbl[] = {
-	{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 }
-};
-
-MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
-
-static struct pci_driver cafe_nand_pci_driver = {
-	.name = "CAF? NAND",
-	.id_table = cafe_nand_tbl,
-	.probe = cafe_nand_probe,
-	.remove = __devexit_p(cafe_nand_remove),
-#ifdef CONFIG_PMx
-	.suspend = cafe_nand_suspend,
-	.resume = cafe_nand_resume,
-#endif
-};
-
-static int cafe_nand_init(void)
-{
-	return pci_register_driver(&cafe_nand_pci_driver);
-}
-
-static void cafe_nand_exit(void)
-{
-	pci_unregister_driver(&cafe_nand_pci_driver);
-}
-module_init(cafe_nand_init);
-module_exit(cafe_nand_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Woodhouse <dwmw2 at infradead.org>");
-MODULE_DESCRIPTION("NAND flash driver for OLPC CAF? chip");
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
new file mode 100644
index 0000000..05f6ec9
--- /dev/null
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -0,0 +1,838 @@
+/*
+ * Driver for One Laptop Per Child ?CAF?? controller, aka Marvell 88ALP01
+ *
+ * Copyright ? 2006 Red Hat, Inc.
+ * Copyright ? 2006 David Woodhouse <dwmw2 at infradead.org>
+ */
+
+#define DEBUG
+
+#include <linux/device.h>
+#undef DEBUG
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/rslib.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <asm/io.h>
+
+#define CAFE_NAND_CTRL1		0x00
+#define CAFE_NAND_CTRL2		0x04
+#define CAFE_NAND_CTRL3		0x08
+#define CAFE_NAND_STATUS	0x0c
+#define CAFE_NAND_IRQ		0x10
+#define CAFE_NAND_IRQ_MASK	0x14
+#define CAFE_NAND_DATA_LEN	0x18
+#define CAFE_NAND_ADDR1		0x1c
+#define CAFE_NAND_ADDR2		0x20
+#define CAFE_NAND_TIMING1	0x24
+#define CAFE_NAND_TIMING2	0x28
+#define CAFE_NAND_TIMING3	0x2c
+#define CAFE_NAND_NONMEM	0x30
+#define CAFE_NAND_ECC_RESULT	0x3C
+#define CAFE_NAND_DMA_CTRL	0x40
+#define CAFE_NAND_DMA_ADDR0	0x44
+#define CAFE_NAND_DMA_ADDR1	0x48
+#define CAFE_NAND_ECC_SYN01	0x50
+#define CAFE_NAND_ECC_SYN23	0x54
+#define CAFE_NAND_ECC_SYN45	0x58
+#define CAFE_NAND_ECC_SYN67	0x5c
+#define CAFE_NAND_READ_DATA	0x1000
+#define CAFE_NAND_WRITE_DATA	0x2000
+
+#define CAFE_GLOBAL_CTRL	0x3004
+#define CAFE_GLOBAL_IRQ		0x3008
+#define CAFE_GLOBAL_IRQ_MASK	0x300c
+#define CAFE_NAND_RESET		0x3034
+
+struct cafe_priv {
+	struct nand_chip nand;
+	struct pci_dev *pdev;
+	void __iomem *mmio;
+	struct rs_control *rs;
+	uint32_t ctl1;
+	uint32_t ctl2;
+	int datalen;
+	int nr_data;
+	int data_pos;
+	int page_addr;
+	dma_addr_t dmaaddr;
+	unsigned char *dmabuf;
+};
+
+static int usedma = 1;
+module_param(usedma, int, 0644);
+
+static int skipbbt = 0;
+module_param(skipbbt, int, 0644);
+
+static int debug = 0;
+module_param(debug, int, 0644);
+
+static int regdebug = 0;
+module_param(regdebug, int, 0644);
+
+static int checkecc = 1;
+module_param(checkecc, int, 0644);
+
+static int numtimings;
+static int timing[3];
+module_param_array(timing, int, &numtimings, 0644);
+
+/* Hrm. Why isn't this already conditional on something in the struct device? */
+#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
+
+/* Make it easier to switch to PIO if we need to */
+#define cafe_readl(cafe, addr)			readl((cafe)->mmio + CAFE_##addr)
+#define cafe_writel(cafe, datum, addr)		writel(datum, (cafe)->mmio + CAFE_##addr)
+
+static int cafe_device_ready(struct mtd_info *mtd)
+{
+	struct cafe_priv *cafe = mtd->priv;
+	int result = !!(cafe_readl(cafe, NAND_STATUS) | 0x40000000);
+	uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
+
+	cafe_writel(cafe, irqs, NAND_IRQ);
+
+	cafe_dev_dbg(&cafe->pdev->dev, "NAND device is%s ready, IRQ %x (%x) (%x,%x)\n",
+		result?"":" not", irqs, cafe_readl(cafe, NAND_IRQ),
+		cafe_readl(cafe, GLOBAL_IRQ), cafe_readl(cafe, GLOBAL_IRQ_MASK));
+
+	return result;
+}
+
+
+static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct cafe_priv *cafe = mtd->priv;
+
+	if (usedma)
+		memcpy(cafe->dmabuf + cafe->datalen, buf, len);
+	else
+		memcpy_toio(cafe->mmio + CAFE_NAND_WRITE_DATA + cafe->datalen, buf, len);
+
+	cafe->datalen += len;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes to write buffer. datalen 0x%x\n",
+		len, cafe->datalen);
+}
+
+static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct cafe_priv *cafe = mtd->priv;
+
+	if (usedma)
+		memcpy(buf, cafe->dmabuf + cafe->datalen, len);
+	else
+		memcpy_fromio(buf, cafe->mmio + CAFE_NAND_READ_DATA + cafe->datalen, len);
+
+	cafe_dev_dbg(&cafe->pdev->dev, "Copy 0x%x bytes from position 0x%x in read buffer.\n",
+		  len, cafe->datalen);
+	cafe->datalen += len;
+}
+
+static uint8_t cafe_read_byte(struct mtd_info *mtd)
+{
+	struct cafe_priv *cafe = mtd->priv;
+	uint8_t d;
+
+	cafe_read_buf(mtd, &d, 1);
+	cafe_dev_dbg(&cafe->pdev->dev, "Read %02x\n", d);
+
+	return d;
+}
+
+static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+			      int column, int page_addr)
+{
+	struct cafe_priv *cafe = mtd->priv;
+	int adrbytes = 0;
+	uint32_t ctl1;
+	uint32_t doneint = 0x80000000;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "cmdfunc %02x, 0x%x, 0x%x\n",
+		command, column, page_addr);
+
+	if (command == NAND_CMD_ERASE2 || command == NAND_CMD_PAGEPROG) {
+		/* Second half of a command we already calculated */
+		cafe_writel(cafe, cafe->ctl2 | 0x100 | command, NAND_CTRL2);
+		ctl1 = cafe->ctl1;
+		cafe->ctl2 &= ~(1<<30);
+		cafe_dev_dbg(&cafe->pdev->dev, "Continue command, ctl1 %08x, #data %d\n",
+			  cafe->ctl1, cafe->nr_data);
+		goto do_command;
+	}
+	/* Reset ECC engine */
+	cafe_writel(cafe, 0, NAND_CTRL2);
+
+	/* Emulate NAND_CMD_READOOB on large-page chips */
+	if (mtd->writesize > 512 &&
+	    command == NAND_CMD_READOOB) {
+		column += mtd->writesize;
+		command = NAND_CMD_READ0;
+	}
+
+	/* FIXME: Do we need to send read command before sending data
+	   for small-page chips, to position the buffer correctly? */
+
+	if (column != -1) {
+		cafe_writel(cafe, column, NAND_ADDR1);
+		adrbytes = 2;
+		if (page_addr != -1)
+			goto write_adr2;
+	} else if (page_addr != -1) {
+		cafe_writel(cafe, page_addr & 0xffff, NAND_ADDR1);
+		page_addr >>= 16;
+	write_adr2:
+		cafe_writel(cafe, page_addr, NAND_ADDR2);
+		adrbytes += 2;
+		if (mtd->size > mtd->writesize << 16)
+			adrbytes++;
+	}
+
+	cafe->data_pos = cafe->datalen = 0;
+
+	/* Set command valid bit */
+	ctl1 = 0x80000000 | command;
+
+	/* Set RD or WR bits as appropriate */
+	if (command == NAND_CMD_READID || command == NAND_CMD_STATUS) {
+		ctl1 |= (1<<26); /* rd */
+		/* Always 5 bytes, for now */
+		cafe->datalen = 4;
+		/* And one address cycle -- even for STATUS, since the controller doesn't work without */
+		adrbytes = 1;
+	} else if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
+		   command == NAND_CMD_READOOB || command == NAND_CMD_RNDOUT) {
+		ctl1 |= 1<<26; /* rd */
+		/* For now, assume just read to end of page */
+		cafe->datalen = mtd->writesize + mtd->oobsize - column;
+	} else if (command == NAND_CMD_SEQIN)
+		ctl1 |= 1<<25; /* wr */
+
+	/* Set number of address bytes */
+	if (adrbytes)
+		ctl1 |= ((adrbytes-1)|8) << 27;
+
+	if (command == NAND_CMD_SEQIN || command == NAND_CMD_ERASE1) {
+		/* Ignore the first command of a pair; the hardware
+		   deals with them both at once, later */
+		cafe->ctl1 = ctl1;
+		cafe_dev_dbg(&cafe->pdev->dev, "Setup for delayed command, ctl1 %08x, dlen %x\n",
+			  cafe->ctl1, cafe->datalen);
+		return;
+	}
+	/* RNDOUT and READ0 commands need a following byte */
+	if (command == NAND_CMD_RNDOUT)
+		cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_RNDOUTSTART, NAND_CTRL2);
+	else if (command == NAND_CMD_READ0 && mtd->writesize > 512)
+		cafe_writel(cafe, cafe->ctl2 | 0x100 | NAND_CMD_READSTART, NAND_CTRL2);
+
+ do_command:
+	cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n",
+		cafe->datalen, ctl1, cafe_readl(cafe, NAND_CTRL2));
+
+	/* NB: The datasheet lies -- we really should be subtracting 1 here */
+	cafe_writel(cafe, cafe->datalen, NAND_DATA_LEN);
+	cafe_writel(cafe, 0x90000000, NAND_IRQ);
+	if (usedma && (ctl1 & (3<<25))) {
+		uint32_t dmactl = 0xc0000000 + cafe->datalen;
+		/* If WR or RD bits set, set up DMA */
+		if (ctl1 & (1<<26)) {
+			/* It's a read */
+			dmactl |= (1<<29);
+			/* ... so it's done when the DMA is done, not just
+			   the command. */
+			doneint = 0x10000000;
+		}
+		cafe_writel(cafe, dmactl, NAND_DMA_CTRL);
+	}
+	cafe->datalen = 0;
+
+	if (unlikely(regdebug)) {
+		int i;
+		printk("About to write command %08x to register 0\n", ctl1);
+		for (i=4; i< 0x5c; i+=4)
+			printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
+	}
+
+	cafe_writel(cafe, ctl1, NAND_CTRL1);
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay(100);
+
+	if (1) {
+		int c;
+		uint32_t irqs;
+
+		for (c = 500000; c != 0; c--) {
+			irqs = cafe_readl(cafe, NAND_IRQ);
+			if (irqs & doneint)
+				break;
+			udelay(1);
+			if (!(c % 100000))
+				cafe_dev_dbg(&cafe->pdev->dev, "Wait for ready, IRQ %x\n", irqs);
+			cpu_relax();
+		}
+		cafe_writel(cafe, doneint, NAND_IRQ);
+		cafe_dev_dbg(&cafe->pdev->dev, "Command %x completed after %d usec, irqs %x (%x)\n",
+			     command, 500000-c, irqs, cafe_readl(cafe, NAND_IRQ));
+	}
+
+	WARN_ON(cafe->ctl2 & (1<<30));
+
+	switch (command) {
+
+	case NAND_CMD_CACHEDPROG:
+	case NAND_CMD_PAGEPROG:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_RNDIN:
+	case NAND_CMD_STATUS:
+	case NAND_CMD_DEPLETE1:
+	case NAND_CMD_RNDOUT:
+	case NAND_CMD_STATUS_ERROR:
+	case NAND_CMD_STATUS_ERROR0:
+	case NAND_CMD_STATUS_ERROR1:
+	case NAND_CMD_STATUS_ERROR2:
+	case NAND_CMD_STATUS_ERROR3:
+		cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
+		return;
+	}
+	nand_wait_ready(mtd);
+	cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
+}
+
+static void cafe_select_chip(struct mtd_info *mtd, int chipnr)
+{
+	//struct cafe_priv *cafe = mtd->priv;
+	//	cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr);
+}
+
+static int cafe_nand_interrupt(int irq, void *id)
+{
+	struct mtd_info *mtd = id;
+	struct cafe_priv *cafe = mtd->priv;
+	uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
+	cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ);
+	if (!irqs)
+		return IRQ_NONE;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "irq, bits %x (%x)\n", irqs, cafe_readl(cafe, NAND_IRQ));
+	return IRQ_HANDLED;
+}
+
+static void cafe_nand_bug(struct mtd_info *mtd)
+{
+	BUG();
+}
+
+static int cafe_nand_write_oob(struct mtd_info *mtd,
+			       struct nand_chip *chip, int page)
+{
+	int status = 0;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+	status = chip->waitfunc(mtd, chip);
+
+	return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+/* Don't use -- use nand_read_oob_std for now */
+static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			      int page, int sndcmd)
+{
+	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	return 1;
+}
+/**
+ * cafe_nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ *
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
+ */
+static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+			       uint8_t *buf)
+{
+	struct cafe_priv *cafe = mtd->priv;
+
+	cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n",
+		     cafe_readl(cafe, NAND_ECC_RESULT),
+		     cafe_readl(cafe, NAND_ECC_SYN01));
+
+	chip->read_buf(mtd, buf, mtd->writesize);
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
+		unsigned short syn[8], pat[4];
+		int pos[4];
+		u8 *oob = chip->oob_poi;
+		int i, n;
+
+		for (i=0; i<8; i+=2) {
+			uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
+			syn[i] = cafe->rs->index_of[tmp & 0xfff];
+			syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff];
+		}
+
+		n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0,
+		                pat);
+
+		for (i = 0; i < n; i++) {
+			int p = pos[i];
+
+			/* The 12-bit symbols are mapped to bytes here */
+
+			if (p > 1374) {
+				/* out of range */
+				n = -1374;
+			} else if (p == 0) {
+				/* high four bits do not correspond to data */
+				if (pat[i] > 0xff)
+					n = -2048;
+				else
+					buf[0] ^= pat[i];
+			} else if (p == 1365) {
+				buf[2047] ^= pat[i] >> 4;
+				oob[0] ^= pat[i] << 4;
+			} else if (p > 1365) {
+				if ((p & 1) == 1) {
+					oob[3*p/2 - 2048] ^= pat[i] >> 4;
+					oob[3*p/2 - 2047] ^= pat[i] << 4;
+				} else {
+					oob[3*p/2 - 2049] ^= pat[i] >> 8;
+					oob[3*p/2 - 2048] ^= pat[i];
+				}
+			} else if ((p & 1) == 1) {
+				buf[3*p/2] ^= pat[i] >> 4;
+				buf[3*p/2 + 1] ^= pat[i] << 4;
+			} else {
+				buf[3*p/2 - 1] ^= pat[i] >> 8;
+				buf[3*p/2] ^= pat[i];
+			}
+		}
+
+		if (n < 0) {
+			dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
+				cafe_readl(cafe, NAND_ADDR2) * 2048);
+			for (i = 0; i < 0x5c; i += 4)
+				printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
+			mtd->ecc_stats.failed++;
+		} else {
+			dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n);
+			mtd->ecc_stats.corrected += n;
+		}
+	}
+
+	return 0;
+}
+
+static struct nand_ecclayout cafe_oobinfo_2048 = {
+	.eccbytes = 14,
+	.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
+	.oobfree = {{14, 50}}
+};
+
+/* Ick. The BBT code really ought to be able to work this bit out
+   for itself from the above, at least for the 2KiB case */
+static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' };
+static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' };
+
+static uint8_t cafe_bbt_pattern_512[] = { 0xBB };
+static uint8_t cafe_mirror_pattern_512[] = { 0xBC };
+
+
+static struct nand_bbt_descr cafe_bbt_main_descr_2048 = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	14,
+	.len = 4,
+	.veroffs = 18,
+	.maxblocks = 4,
+	.pattern = cafe_bbt_pattern_2048
+};
+
+static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	14,
+	.len = 4,
+	.veroffs = 18,
+	.maxblocks = 4,
+	.pattern = cafe_mirror_pattern_2048
+};
+
+static struct nand_ecclayout cafe_oobinfo_512 = {
+	.eccbytes = 14,
+	.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
+	.oobfree = {{14, 2}}
+};
+
+static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	14,
+	.len = 1,
+	.veroffs = 15,
+	.maxblocks = 4,
+	.pattern = cafe_bbt_pattern_512
+};
+
+static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	14,
+	.len = 1,
+	.veroffs = 15,
+	.maxblocks = 4,
+	.pattern = cafe_mirror_pattern_512
+};
+
+
+static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
+					  struct nand_chip *chip, const uint8_t *buf)
+{
+	struct cafe_priv *cafe = mtd->priv;
+
+	chip->write_buf(mtd, buf, mtd->writesize);
+	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	/* Set up ECC autogeneration */
+	cafe->ctl2 |= (1<<30);
+}
+
+static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf, int page, int cached, int raw)
+{
+	int status;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+	if (unlikely(raw))
+		chip->ecc.write_page_raw(mtd, chip, buf);
+	else
+		chip->ecc.write_page(mtd, chip, buf);
+
+	/*
+	 * Cached progamming disabled for now, Not sure if its worth the
+	 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+	 */
+	cached = 0;
+
+	if (!cached || !(chip->options & NAND_CACHEPRG)) {
+
+		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+		status = chip->waitfunc(mtd, chip);
+		/*
+		 * See if operation failed and additional status checks are
+		 * available
+		 */
+		if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+			status = chip->errstat(mtd, chip, FL_WRITING, status,
+					       page);
+
+		if (status & NAND_STATUS_FAIL)
+			return -EIO;
+	} else {
+		chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+		status = chip->waitfunc(mtd, chip);
+	}
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+	/* Send command to read back the data */
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+	if (chip->verify_buf(mtd, buf, mtd->writesize))
+		return -EIO;
+#endif
+	return 0;
+}
+
+static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	return 0;
+}
+
+/* F_2[X]/(X**6+X+1)  */
+static unsigned short __devinit gf64_mul(u8 a, u8 b)
+{
+	u8 c;
+	unsigned int i;
+
+	c = 0;
+	for (i = 0; i < 6; i++) {
+		if (a & 1)
+			c ^= b;
+		a >>= 1;
+		b <<= 1;
+		if ((b & 0x40) != 0)
+			b ^= 0x43;
+	}
+
+	return c;
+}
+
+/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X]  */
+static u16 __devinit gf4096_mul(u16 a, u16 b)
+{
+	u8 ah, al, bh, bl, ch, cl;
+
+	ah = a >> 6;
+	al = a & 0x3f;
+	bh = b >> 6;
+	bl = b & 0x3f;
+
+	ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl);
+	cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl);
+
+	return (ch << 6) ^ cl;
+}
+
+static int __devinit cafe_mul(int x)
+{
+	if (x == 0)
+		return 1;
+	return gf4096_mul(x, 0xe01);
+}
+
+static int __devinit cafe_nand_probe(struct pci_dev *pdev,
+				     const struct pci_device_id *ent)
+{
+	struct mtd_info *mtd;
+	struct cafe_priv *cafe;
+	uint32_t ctrl;
+	int err = 0;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	pci_set_master(pdev);
+
+	mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL);
+	if (!mtd) {
+		dev_warn(&pdev->dev, "failed to alloc mtd_info\n");
+		return  -ENOMEM;
+	}
+	cafe = (void *)(&mtd[1]);
+
+	mtd->priv = cafe;
+	mtd->owner = THIS_MODULE;
+
+	cafe->pdev = pdev;
+	cafe->mmio = pci_iomap(pdev, 0, 0);
+	if (!cafe->mmio) {
+		dev_warn(&pdev->dev, "failed to iomap\n");
+		err = -ENOMEM;
+		goto out_free_mtd;
+	}
+	cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers),
+					  &cafe->dmaaddr, GFP_KERNEL);
+	if (!cafe->dmabuf) {
+		err = -ENOMEM;
+		goto out_ior;
+	}
+	cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
+
+	cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
+	if (!cafe->rs) {
+		err = -ENOMEM;
+		goto out_ior;
+	}
+
+	cafe->nand.cmdfunc = cafe_nand_cmdfunc;
+	cafe->nand.dev_ready = cafe_device_ready;
+	cafe->nand.read_byte = cafe_read_byte;
+	cafe->nand.read_buf = cafe_read_buf;
+	cafe->nand.write_buf = cafe_write_buf;
+	cafe->nand.select_chip = cafe_select_chip;
+
+	cafe->nand.chip_delay = 0;
+
+	/* Enable the following for a flash based bad block table */
+	cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS;
+
+	if (skipbbt) {
+		cafe->nand.options |= NAND_SKIP_BBTSCAN;
+		cafe->nand.block_bad = cafe_nand_block_bad;
+	}
+
+	if (numtimings && numtimings != 3) {
+		dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings);
+	}
+
+	if (numtimings == 3) {
+		cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
+			     timing[0], timing[1], timing[2]);
+	} else {
+		timing[0] = cafe_readl(cafe, NAND_TIMING1);
+		timing[1] = cafe_readl(cafe, NAND_TIMING2);
+		timing[2] = cafe_readl(cafe, NAND_TIMING3);
+
+		if (timing[0] | timing[1] | timing[2]) {
+			cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n",
+				     timing[0], timing[1], timing[2]);
+		} else {
+			dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
+			timing[0] = timing[1] = timing[2] = 0xffffffff;
+		}
+	}
+
+	/* Start off by resetting the NAND controller completely */
+	cafe_writel(cafe, 1, NAND_RESET);
+	cafe_writel(cafe, 0, NAND_RESET);
+
+	cafe_writel(cafe, timing[0], NAND_TIMING1);
+	cafe_writel(cafe, timing[1], NAND_TIMING2);
+	cafe_writel(cafe, timing[2], NAND_TIMING3);
+
+	cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
+	err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
+			  "CAFE NAND", mtd);
+	if (err) {
+		dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
+		goto out_free_dma;
+	}
+
+	/* Disable master reset, enable NAND clock */
+	ctrl = cafe_readl(cafe, GLOBAL_CTRL);
+	ctrl &= 0xffffeff0;
+	ctrl |= 0x00007000;
+	cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
+	cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
+	cafe_writel(cafe, 0, NAND_DMA_CTRL);
+
+	cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
+	cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
+
+	/* Set up DMA address */
+	cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
+	if (sizeof(cafe->dmaaddr) > 4)
+		/* Shift in two parts to shut the compiler up */
+		cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1);
+	else
+		cafe_writel(cafe, 0, NAND_DMA_ADDR1);
+
+	cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
+		cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
+
+	/* Enable NAND IRQ in global IRQ mask register */
+	cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+	cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
+		cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
+
+	/* Scan to find existence of the device */
+	if (nand_scan_ident(mtd, 1)) {
+		err = -ENXIO;
+		goto out_irq;
+	}
+
+	cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
+	if (mtd->writesize == 2048)
+		cafe->ctl2 |= 1<<29; /* 2KiB page size */
+
+	/* Set up ECC according to the type of chip we found */
+	if (mtd->writesize == 2048) {
+		cafe->nand.ecc.layout = &cafe_oobinfo_2048;
+		cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
+		cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
+	} else if (mtd->writesize == 512) {
+		cafe->nand.ecc.layout = &cafe_oobinfo_512;
+		cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
+		cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
+	} else {
+		printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
+		       mtd->writesize);
+		goto out_irq;
+	}
+	cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
+	cafe->nand.ecc.size = mtd->writesize;
+	cafe->nand.ecc.bytes = 14;
+	cafe->nand.ecc.hwctl  = (void *)cafe_nand_bug;
+	cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
+	cafe->nand.ecc.correct  = (void *)cafe_nand_bug;
+	cafe->nand.write_page = cafe_nand_write_page;
+	cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
+	cafe->nand.ecc.write_oob = cafe_nand_write_oob;
+	cafe->nand.ecc.read_page = cafe_nand_read_page;
+	cafe->nand.ecc.read_oob = cafe_nand_read_oob;
+
+	err = nand_scan_tail(mtd);
+	if (err)
+		goto out_irq;
+
+	pci_set_drvdata(pdev, mtd);
+	add_mtd_device(mtd);
+	goto out;
+
+ out_irq:
+	/* Disable NAND IRQ in global IRQ mask register */
+	cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
+	free_irq(pdev->irq, mtd);
+ out_free_dma:
+	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+ out_ior:
+	pci_iounmap(pdev, cafe->mmio);
+ out_free_mtd:
+	kfree(mtd);
+ out:
+	return err;
+}
+
+static void __devexit cafe_nand_remove(struct pci_dev *pdev)
+{
+	struct mtd_info *mtd = pci_get_drvdata(pdev);
+	struct cafe_priv *cafe = mtd->priv;
+
+	del_mtd_device(mtd);
+	/* Disable NAND IRQ in global IRQ mask register */
+	cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
+	free_irq(pdev->irq, mtd);
+	nand_release(mtd);
+	free_rs(cafe->rs);
+	pci_iounmap(pdev, cafe->mmio);
+	dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+	kfree(mtd);
+}
+
+static struct pci_device_id cafe_nand_tbl[] = {
+	{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 }
+};
+
+MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
+
+static struct pci_driver cafe_nand_pci_driver = {
+	.name = "CAF? NAND",
+	.id_table = cafe_nand_tbl,
+	.probe = cafe_nand_probe,
+	.remove = __devexit_p(cafe_nand_remove),
+#ifdef CONFIG_PMx
+	.suspend = cafe_nand_suspend,
+	.resume = cafe_nand_resume,
+#endif
+};
+
+static int cafe_nand_init(void)
+{
+	return pci_register_driver(&cafe_nand_pci_driver);
+}
+
+static void cafe_nand_exit(void)
+{
+	pci_unregister_driver(&cafe_nand_pci_driver);
+}
+module_init(cafe_nand_init);
+module_exit(cafe_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2 at infradead.org>");
+MODULE_DESCRIPTION("NAND flash driver for OLPC CAF? chip");


From linux-mtd at lists.infradead.org  Thu May  3 02:59:02 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Thu, 03 May 2007 07:59:02 +0100
Subject: [MTD] [NAND] at91_nand.c: CMDLINE_PARTS support
Message-ID: <E1HjVHK-0003Vk-9C@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=693ef66d8914f50cb899b5268676ea508d1f3178
Commit:     693ef66d8914f50cb899b5268676ea508d1f3178
Parent:     1a12f46af1af1a4b7b9c7ae7ab2c8ded3481a4ba
Author:     Andrew Victor <andrew at sanpeople.com>
AuthorDate: Thu May 3 08:16:44 2007 +0200
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Thu May 3 07:40:41 2007 +0100

    [MTD] [NAND] at91_nand.c: CMDLINE_PARTS support
    
    This patch allows you to specify at91_nand partitions on the
    kernel command line using the mtdparts variable, if
    CONFIG_MTD_CMDLINE_PARTS is set.
    
    Signed-off-by: Frank Mandarino <fmandarino at endrelia.com>
    Signed-off-by: Andrew Victor <andrew at sanpeople.com>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/nand/at91_nand.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c
index 14b80cc..512e999 100644
--- a/drivers/mtd/nand/at91_nand.c
+++ b/drivers/mtd/nand/at91_nand.c
@@ -82,6 +82,10 @@ static void at91_nand_disable(struct at91_nand_host *host)
 		at91_set_gpio_value(host->board->enable_pin, 1);
 }
 
+#ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
 /*
  * Probe for the NAND device.
  */
@@ -151,6 +155,12 @@ static int __init at91_nand_probe(struct platform_device *pdev)
 #ifdef CONFIG_MTD_PARTITIONS
 	if (host->board->partition_info)
 		partitions = host->board->partition_info(mtd->size, &num_partitions);
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+	else {
+		mtd->name = "at91_nand";
+		num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0);
+	}
+#endif
 
 	if ((!partitions) || (num_partitions == 0)) {
 		printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n");


From linux-mtd at lists.infradead.org  Thu May  3 02:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Thu, 03 May 2007 07:59:01 +0100
Subject: [MTD] [NAND] Tidy up handling of page number in nand_block_bad()
Message-ID: <E1HjVHJ-0003VG-UH@pentafluge.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=1a12f46af1af1a4b7b9c7ae7ab2c8ded3481a4ba
Commit:     1a12f46af1af1a4b7b9c7ae7ab2c8ded3481a4ba
Parent:     4839f0481d8b985aabd6653ba42cf09e2abcc2bd
Author:     Thomas Knobloch <knobloch at siemens.com>
AuthorDate: Thu May 3 07:39:37 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Thu May 3 07:39:37 2007 +0100

    [MTD] [NAND] Tidy up handling of page number in nand_block_bad()
    
    Further to the previous patch fixing the calculation of page number,
    both branches are using the same result. Clean up the function
    accordingly, calculating it (and also masking with pagemask) only in one
    place.
    
    Signed-off-by: Thomas Knobloch <knobloch at siemens.com>
    Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/nand/nand_base.c |   11 +++++------
 1 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 04de315..7e68203 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -303,28 +303,27 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 	struct nand_chip *chip = mtd->priv;
 	u16 bad;
 
+	page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
 	if (getchip) {
-		page = (int)(ofs >> chip->page_shift);
 		chipnr = (int)(ofs >> chip->chip_shift);
 
 		nand_get_device(chip, mtd, FL_READING);
 
 		/* Select the NAND device */
 		chip->select_chip(mtd, chipnr);
-	} else
-		page = (int)(ofs >> chip->page_shift);
+	}
 
 	if (chip->options & NAND_BUSWIDTH_16) {
 		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
-			      page & chip->pagemask);
+			      page);
 		bad = cpu_to_le16(chip->read_word(mtd));
 		if (chip->badblockpos & 0x1)
 			bad >>= 8;
 		if ((bad & 0xFF) != 0xff)
 			res = 1;
 	} else {
-		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
-			      page & chip->pagemask);
+		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
 		if (chip->read_byte(mtd) != 0xff)
 			res = 1;
 	}


From linux-mtd at lists.infradead.org  Sat May  5 04:53:19 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Sat, 05 May 2007 04:53:19 -0400
Subject: [JFFS2] Don't advance c->wbuf_ofs to next eraseblock after wbuf flush
Message-ID: <E1HkG11-0001Bf-OF@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=3fddb6c985e3823c991399840d2d5ef5940e1b60
Commit:     3fddb6c985e3823c991399840d2d5ef5940e1b60
Parent:     693ef66d8914f50cb899b5268676ea508d1f3178
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Sat May 5 09:52:49 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Sat May 5 09:52:49 2007 +0100

    [JFFS2] Don't advance c->wbuf_ofs to next eraseblock after wbuf flush
    
    After flushing the last page of an eraseblock, don't leave the
    wbuf 'offset' field pointing at the start of the next physical
    eraseblock. This was causing a BUG() on NOR-ECC (Sibley) flash, where
    we start writing a little further in, after the cleanmarker.
    
    Debugged by Alexander Belyakov <abelyako at googlemail.com>
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 fs/jffs2/wbuf.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index c556e85..91d1d0f 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -637,7 +637,10 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
 
 	memset(c->wbuf,0xff,c->wbuf_pagesize);
 	/* adjust write buffer offset, else we get a non contiguous write bug */
-	c->wbuf_ofs += c->wbuf_pagesize;
+	if (SECTOR_ADDR(c->wbuf_ofs) == SECTOR_ADDR(c->wbuf_ofs+c->wbuf_pagesize))
+		c->wbuf_ofs += c->wbuf_pagesize;
+	else
+		c->wbuf_ofs = 0xffffffff;
 	c->wbuf_len = 0;
 	return 0;
 }


From linux-mtd at lists.infradead.org  Sat May  5 11:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Sat, 05 May 2007 11:59:01 -0400
Subject: [JFFS2] Remember to calculate overlap on nodes which replace older
	nodes
Message-ID: <E1HkMez-0005g3-UN@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=1123e2a85941c7f506bd42c91c7e9ab74fc42d79
Commit:     1123e2a85941c7f506bd42c91c7e9ab74fc42d79
Parent:     3fddb6c985e3823c991399840d2d5ef5940e1b60
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Sat May 5 16:29:34 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Sat May 5 16:29:34 2007 +0100

    [JFFS2] Remember to calculate overlap on nodes which replace older nodes
    
    This fixes a problem Artem found with the integck test tool -- we
    weren't correctly keeping track of the 'overlap' flag in some cases,
    which led to the nodes being played back in an incorrect order and file
    corruption.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 fs/jffs2/readinode.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index 6aff389..b0645ac 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -276,10 +276,9 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				/* Who cares if the new one is good; keep it for now anyway. */
 				rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
 				/* Same overlapping from in front and behind */
-				tn->overlapped = this->overlapped;
 				jffs2_kill_tn(c, this);
 				dbg_readinode("Like new node. Throw away old\n");
-				return 0;
+				goto calc_overlaps;
 			}
 		}
 		if (this->version < tn->version &&
@@ -293,7 +292,6 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 			}
 			/* ... and is good. Kill 'this'... */
 			rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
-			tn->overlapped = this->overlapped;
 			jffs2_kill_tn(c, this);
 			/* ... and any subsequent nodes which are also overlapped */
 			this = tn_next(tn);
@@ -309,7 +307,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				this = next;
 			}
 			dbg_readinode("Done inserting new\n");
-			return 0;
+			goto calc_overlaps;
 		}
 		if (this->version > tn->version &&
 		    this->fn->ofs <= tn->fn->ofs &&
@@ -321,6 +319,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				return 0;
 			}
 			/* ... but 'this' was bad. Replace it... */
+			tn->overlapped = this->overlapped;
 			rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
 			dbg_readinode("Bad CRC on old overlapping node. Kill it\n");
 			jffs2_kill_tn(c, this);
@@ -359,6 +358,8 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 		rb_link_node(&tn->rb, &insert_point->rb, link);
 		rb_insert_color(&tn->rb, &rii->tn_root);
 	}
+
+ calc_overlaps:
 	/* If there's anything behind that overlaps us, note it */
 	this = tn_prev(tn);
 	if (this) {
@@ -483,7 +484,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
 			vers_next = tn_prev(this);
 			eat_last(&ver_root, &this->rb);
 			if (check_tn_node(c, this)) {
-				dbg_readinode("node ver %x, 0x%x-0x%x failed CRC\n",
+				dbg_readinode("node ver %d, 0x%x-0x%x failed CRC\n",
 					     this->version, this->fn->ofs,
 					     this->fn->ofs+this->fn->size);
 				jffs2_kill_tn(c, this);
@@ -496,7 +497,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
 					high_ver = this->version;
 					rii->latest_ref = this->fn->raw;
 				}
-				dbg_readinode("Add %p (v %x, 0x%x-0x%x, ov %d) to fragtree\n",
+				dbg_readinode("Add %p (v %d, 0x%x-0x%x, ov %d) to fragtree\n",
 					     this, this->version, this->fn->ofs,
 					     this->fn->ofs+this->fn->size, this->overlapped);
 
@@ -850,7 +851,7 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 		return ret;
 	}
 #ifdef JFFS2_DBG_READINODE_MESSAGES
-	dbg_readinode("After adding ver %d:\n", tn->version);
+	dbg_readinode("After adding ver %d:\n", je32_to_cpu(rd->version));
 	tn = tn_first(&rii->tn_root);
 	while (tn) {
 		dbg_readinode("%p: v %d r 0x%x-0x%x ov %d\n",


From linux-mtd at lists.infradead.org  Sun May  6 09:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Sun, 06 May 2007 09:59:01 -0400
Subject: [JFFS2] Remove broken insert_point optimisation in
	jffs2_add_tn_to_tree()
Message-ID: <E1HkhGP-0002vp-FU@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=96dd8d25d1ca8c233bd47752349d27a631c18719
Commit:     96dd8d25d1ca8c233bd47752349d27a631c18719
Parent:     1123e2a85941c7f506bd42c91c7e9ab74fc42d79
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Sun May 6 14:41:40 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Sun May 6 14:41:40 2007 +0100

    [JFFS2] Remove broken insert_point optimisation in jffs2_add_tn_to_tree()
    
    The original code would remember, during the first pass over the tree,
    a suitable place to start the insertion from when we eventually come
    to add a new node.
    
    The optimisation was broken, and we sometimes ended up inserting a new
    node in the wrong place because we started the insertion from the wrong
    point.
    
    Just ditch the optimisation and start the insertion from the root of the
    tree, for now. I'll try it again when I'm feeling cleverer.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 fs/jffs2/readinode.c |   25 +++++++------------------
 1 files changed, 7 insertions(+), 18 deletions(-)

diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index b0645ac..f461604 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -219,7 +219,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				struct jffs2_tmp_dnode_info *tn)
 {
 	uint32_t fn_end = tn->fn->ofs + tn->fn->size;
-	struct jffs2_tmp_dnode_info *insert_point = NULL, *this;
+	struct jffs2_tmp_dnode_info *this;
 
 	dbg_readinode("insert fragment %#04x-%#04x, ver %u\n", tn->fn->ofs, fn_end, tn->version);
 
@@ -248,9 +248,6 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 		return 0;
 	}
 
-	/* If we add a new node it'll be somewhere under here. */
-	insert_point = this;
-
 	/* If the node is coincident with another at a lower address,
 	   back up until the other node is found. It may be relevant */
 	while (tn->overlapped)
@@ -325,24 +322,16 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 			jffs2_kill_tn(c, this);
 			return 0;
 		}
-		/* We want to be inserted under the last node which is
-		   either at a lower offset _or_ has a smaller range */
-		if (this->fn->ofs < tn->fn->ofs ||
-		    (this->fn->ofs == tn->fn->ofs &&
-		     this->fn->size <= tn->fn->size))
-			insert_point = this;
 
 		this = tn_next(this);
 	}
-	dbg_readinode("insert_point %p, ver %d, 0x%x-0x%x, ov %d\n",
-		      insert_point, insert_point->version, insert_point->fn->ofs,
-		      insert_point->fn->ofs+insert_point->fn->size,
-		      insert_point->overlapped);
+
 	/* We neither completely obsoleted nor were completely
-	   obsoleted by an earlier node. Insert under insert_point */
+	   obsoleted by an earlier node. Insert into the tree */
 	{
-		struct rb_node *parent = &insert_point->rb;
-		struct rb_node **link = &parent;
+		struct rb_node *parent;
+		struct rb_node **link = &rii->tn_root.rb_node;
+		struct jffs2_tmp_dnode_info *insert_point;
 
 		while (*link) {
 			parent = *link;
@@ -458,7 +447,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
 	this = tn_last(&rii->tn_root);
 	while (this) {
 		dbg_readinode("tn %p ver %d range 0x%x-0x%x ov %d\n", this, this->version, this->fn->ofs,
-			     this->fn->ofs+this->fn->size, this->overlapped);
+			      this->fn->ofs+this->fn->size, this->overlapped);
 		this = tn_prev(this);
 	}
 #endif


From linux-mtd at lists.infradead.org  Mon May  7 08:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Mon, 07 May 2007 08:59:01 -0400
Subject: [JFFS2] Remove another bogus optimisation in jffs2_add_tn_to_tree()
Message-ID: <E1Hl2nt-0005Wp-4h@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=fcf3cafb3e7e7750f4876571492594c3a4854ee5
Commit:     fcf3cafb3e7e7750f4876571492594c3a4854ee5
Parent:     96dd8d25d1ca8c233bd47752349d27a631c18719
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Mon May 7 13:16:13 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Mon May 7 13:16:13 2007 +0100

    [JFFS2] Remove another bogus optimisation in jffs2_add_tn_to_tree()
    
    We attempted to insert new nodes into the tree by just using
    rb_replace_node to let them replace an earlier node which they
    completely overlapped. However, that could place the new node into the
    wrong place in the tree, since its start could be node only before the
    start of the victim, but before the node _before_ the victim in the tree
    (if that previous node actually ends _after_ the new node, thus isn't
    entirely overlapped and wasn't itself chosen to be the victim).
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 fs/jffs2/readinode.c |   23 +++++++++--------------
 1 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index f461604..bd31d49 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -221,7 +221,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 	uint32_t fn_end = tn->fn->ofs + tn->fn->size;
 	struct jffs2_tmp_dnode_info *this;
 
-	dbg_readinode("insert fragment %#04x-%#04x, ver %u\n", tn->fn->ofs, fn_end, tn->version);
+	dbg_readinode("insert fragment %#04x-%#04x, ver %u at %08x\n", tn->fn->ofs, fn_end, tn->version, ref_offset(tn->fn->raw));
 
 	/* If a node has zero dsize, we only have to keep if it if it might be the
 	   node with highest version -- i.e. the one which will end up as f->metadata.
@@ -271,11 +271,11 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				return 0;
 			} else {
 				/* Who cares if the new one is good; keep it for now anyway. */
+				dbg_readinode("Like new node. Throw away old\n");
 				rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
-				/* Same overlapping from in front and behind */
 				jffs2_kill_tn(c, this);
-				dbg_readinode("Like new node. Throw away old\n");
-				goto calc_overlaps;
+				/* Same overlapping from in front and behind */
+				return 0;
 			}
 		}
 		if (this->version < tn->version &&
@@ -287,11 +287,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				jffs2_kill_tn(c, tn);
 				return 0;
 			}
-			/* ... and is good. Kill 'this'... */
-			rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
-			jffs2_kill_tn(c, this);
-			/* ... and any subsequent nodes which are also overlapped */
-			this = tn_next(tn);
+			/* ... and is good. Kill 'this' and any subsequent nodes which are also overlapped */
 			while (this && this->fn->ofs + this->fn->size < fn_end) {
 				struct jffs2_tmp_dnode_info *next = tn_next(this);
 				if (this->version < tn->version) {
@@ -303,8 +299,8 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				}
 				this = next;
 			}
-			dbg_readinode("Done inserting new\n");
-			goto calc_overlaps;
+			dbg_readinode("Done killing overlapped nodes\n");
+			break;
 		}
 		if (this->version > tn->version &&
 		    this->fn->ofs <= tn->fn->ofs &&
@@ -316,11 +312,10 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				return 0;
 			}
 			/* ... but 'this' was bad. Replace it... */
-			tn->overlapped = this->overlapped;
-			rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
 			dbg_readinode("Bad CRC on old overlapping node. Kill it\n");
+			tn_erase(this, &rii->tn_root);
 			jffs2_kill_tn(c, this);
-			return 0;
+			break;
 		}
 
 		this = tn_next(this);


From linux-mtd at lists.infradead.org  Mon May  7 19:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Mon, 07 May 2007 19:59:01 -0400
Subject: [JFFS2] Simplify and clean up jffs2_add_tn_to_tree() some more.
Message-ID: <E1HlD6b-0002as-7l@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=1c97964520503c60aa91680d4b525236c2885a36
Commit:     1c97964520503c60aa91680d4b525236c2885a36
Parent:     fcf3cafb3e7e7750f4876571492594c3a4854ee5
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Tue May 8 00:19:54 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Tue May 8 00:19:54 2007 +0100

    [JFFS2] Simplify and clean up jffs2_add_tn_to_tree() some more.
    
    Fixing at least a couple more bugs in the process.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 fs/jffs2/readinode.c |   29 ++++++++++++-----------------
 1 files changed, 12 insertions(+), 17 deletions(-)

diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
index bd31d49..4884d5e 100644
--- a/fs/jffs2/readinode.c
+++ b/fs/jffs2/readinode.c
@@ -240,20 +240,16 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 
 	/* Find the earliest node which _may_ be relevant to this one */
 	this = jffs2_lookup_tn(&rii->tn_root, tn->fn->ofs);
-	if (!this) {
-		/* First addition to empty tree. $DEITY how I love the easy cases */
-		rb_link_node(&tn->rb, NULL, &rii->tn_root.rb_node);
-		rb_insert_color(&tn->rb, &rii->tn_root);
-		dbg_readinode("keep new frag\n");
-		return 0;
-	}
-
-	/* If the node is coincident with another at a lower address,
-	   back up until the other node is found. It may be relevant */
-	while (tn->overlapped)
-		tn = tn_prev(tn);
+	if (this) {
+		/* If the node is coincident with another at a lower address,
+		   back up until the other node is found. It may be relevant */
+		while (this->overlapped)
+			this = tn_prev(this);
 
-	dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole");
+		/* First node should never be marked overlapped */
+		BUG_ON(!this);
+		dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole");
+	}
 
 	while (this) {
 		if (this->fn->ofs > fn_end)
@@ -288,7 +284,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				return 0;
 			}
 			/* ... and is good. Kill 'this' and any subsequent nodes which are also overlapped */
-			while (this && this->fn->ofs + this->fn->size < fn_end) {
+			while (this && this->fn->ofs + this->fn->size <= fn_end) {
 				struct jffs2_tmp_dnode_info *next = tn_next(this);
 				if (this->version < tn->version) {
 					tn_erase(this, &rii->tn_root);
@@ -300,7 +296,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 				this = next;
 			}
 			dbg_readinode("Done killing overlapped nodes\n");
-			break;
+			continue;
 		}
 		if (this->version > tn->version &&
 		    this->fn->ofs <= tn->fn->ofs &&
@@ -326,7 +322,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 	{
 		struct rb_node *parent;
 		struct rb_node **link = &rii->tn_root.rb_node;
-		struct jffs2_tmp_dnode_info *insert_point;
+		struct jffs2_tmp_dnode_info *insert_point = NULL;
 
 		while (*link) {
 			parent = *link;
@@ -343,7 +339,6 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 		rb_insert_color(&tn->rb, &rii->tn_root);
 	}
 
- calc_overlaps:
 	/* If there's anything behind that overlaps us, note it */
 	this = tn_prev(tn);
 	if (this) {


From linux-mtd at lists.infradead.org  Mon May  7 19:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Mon, 07 May 2007 19:59:01 -0400
Subject: [MTD] [NAND] platform NAND driver: update header
Message-ID: <E1HlD6b-0002bD-BO@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=972edcb79ec8c8512ed5b29ca6718065328d6992
Commit:     972edcb79ec8c8512ed5b29ca6718065328d6992
Parent:     1c97964520503c60aa91680d4b525236c2885a36
Author:     Vitaly Wool <vitalywool at gmail.com>
AuthorDate: Sun May 6 18:46:57 2007 +0400
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Tue May 8 00:40:32 2007 +0100

    [MTD] [NAND] platform NAND driver: update header
    
    This patch extends nand.h in order to enable platform NAND driver.
    
    Signed-off-by: Vitaly Wool <vitalywool at gmail.com>
    Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 include/linux/mtd/nand.h |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index cf197ad..d2365c8 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -560,6 +560,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
  * @chip_delay:		R/B delay value in us
  * @options:		Option flags, e.g. 16bit buswidth
  * @ecclayout:		ecc layout info structure
+ * @part_probe_types:	NULL-terminated array of probe types
  * @priv:		hardware controller specific settings
  */
 struct platform_nand_chip {
@@ -570,6 +571,7 @@ struct platform_nand_chip {
 	struct nand_ecclayout	*ecclayout;
 	int			chip_delay;
 	unsigned int		options;
+	const char		**part_probe_types;
 	void			*priv;
 };
 
@@ -578,6 +580,8 @@ struct platform_nand_chip {
  * @hwcontrol:		platform specific hardware control structure
  * @dev_ready:		platform specific function to read ready/busy pin
  * @select_chip:	platform specific chip select function
+ * @cmd_ctrl:		platform specific function for controlling
+ *			ALE/CLE/nCE. Also used to write command and address
  * @priv:		private data to transport driver specific settings
  *
  * All fields are optional and depend on the hardware driver requirements
@@ -586,9 +590,21 @@ struct platform_nand_ctrl {
 	void		(*hwcontrol)(struct mtd_info *mtd, int cmd);
 	int		(*dev_ready)(struct mtd_info *mtd);
 	void		(*select_chip)(struct mtd_info *mtd, int chip);
+	void		(*cmd_ctrl)(struct mtd_info *mtd, int dat,
+				    unsigned int ctrl);
 	void		*priv;
 };
 
+/**
+ * struct platform_nand_data - container structure for platform-specific data
+ * @chip:		chip level chip structure
+ * @ctrl:		controller level device structure
+ */
+struct platform_nand_data {
+	struct platform_nand_chip	chip;
+	struct platform_nand_ctrl	ctrl;
+};
+
 /* Some helpers to access the data structures */
 static inline
 struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd)


From linux-mtd at lists.infradead.org  Mon May  7 19:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Mon, 07 May 2007 19:59:01 -0400
Subject: [MTD] [NAND] platform NAND driver: add driver
Message-ID: <E1HlD6b-0002bX-Dg@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=711fdf627ce1374796632f16acec1ab63d11e38f
Commit:     711fdf627ce1374796632f16acec1ab63d11e38f
Parent:     972edcb79ec8c8512ed5b29ca6718065328d6992
Author:     Vitaly Wool <vitalywool at gmail.com>
AuthorDate: Sun May 6 19:31:18 2007 +0400
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Tue May 8 00:41:55 2007 +0100

    [MTD] [NAND] platform NAND driver: add driver
    
    This patch adds support for generic platform NAND driver.
    Updated after tglx's review/discussion in IRC #mtd channel.
    
    Signed-off-by: Vitaly Wool <vitalywool at gmail.com>
    Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/nand/Kconfig     |    9 +++
 drivers/mtd/nand/Makefile    |    1 +
 drivers/mtd/nand/plat_nand.c |  150 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 160 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 03ed254..f1d60b6 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -272,4 +272,13 @@ config MTD_NAND_NANDSIM
 	  The simulator may simulate various NAND flash chips for the
 	  MTD nand layer.
 
+config MTD_NAND_PLATFORM
+	tristate "Support for generic platform NAND driver"
+	depends on MTD_NAND
+	help
+	  This implements a generic NAND driver for on-SOC platform
+	  devices. You will need to provide platform-specific functions
+	  via platform_data.
+
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index de1424b..edba1db 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -26,5 +26,6 @@ obj-$(CONFIG_MTD_NAND_NDFC)		+= ndfc.o
 obj-$(CONFIG_MTD_NAND_AT91)		+= at91_nand.o
 obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)	+= excite_nandflash.o
+obj-$(CONFIG_MTD_NAND_PLATFORM)		+= plat_nand.o
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
new file mode 100644
index 0000000..cd725fc
--- /dev/null
+++ b/drivers/mtd/nand/plat_nand.c
@@ -0,0 +1,150 @@
+/*
+ * Generic NAND driver
+ *
+ * Author: Vitaly Wool <vitalywool at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+struct plat_nand_data {
+	struct nand_chip	chip;
+	struct mtd_info		mtd;
+	void __iomem		*io_base;
+#ifdef CONFIG_MTD_PARTITIONS
+	int			nr_parts;
+	struct mtd_partition	*parts;
+#endif
+};
+
+/*
+ * Probe for the NAND device.
+ */
+static int __init plat_nand_probe(struct platform_device *pdev)
+{
+	struct platform_nand_data *pdata = pdev->dev.platform_data;
+	struct plat_nand_data *data;
+	int res = 0;
+
+	/* Allocate memory for the device structure (and zero it) */
+	data = kzalloc(sizeof(struct plat_nand_data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&pdev->dev, "failed to allocate device structure.\n");
+		return -ENOMEM;
+	}
+
+	data->io_base = ioremap(pdev->resource[0].start,
+				pdev->resource[0].end - pdev->resource[0].start + 1);
+	if (data->io_base == NULL) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		kfree(data);
+		return -EIO;
+	}
+
+	data->chip.priv = &data;
+	data->mtd.priv = &data->chip;
+	data->mtd.owner = THIS_MODULE;
+
+	data->chip.IO_ADDR_R = data->io_base;
+	data->chip.IO_ADDR_W = data->io_base;
+	data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl;
+	data->chip.dev_ready = pdata->ctrl.dev_ready;
+	data->chip.select_chip = pdata->ctrl.select_chip;
+	data->chip.chip_delay = pdata->chip.chip_delay;
+	data->chip.options |= pdata->chip.options;
+
+	data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
+	data->chip.ecc.layout = pdata->chip.ecclayout;
+	data->chip.ecc.mode = NAND_ECC_SOFT;
+
+	platform_set_drvdata(pdev, data);
+
+	/* Scan to find existance of the device */
+	if (nand_scan(&data->mtd, 1)) {
+		res = -ENXIO;
+		goto out;
+	}
+
+#ifdef CONFIG_MTD_PARTITIONS
+	if (pdata->chip.part_probe_types) {
+		res = parse_mtd_partitions(&data->mtd,
+					pdata->chip.part_probe_types,
+					&data->parts, 0);
+		if (res > 0) {
+			add_mtd_partitions(&data->mtd, data->parts, res);
+			return 0;
+		}
+	}
+	if (pdata->chip.partitions) {
+		data->parts = pdata->chip.partitions;
+		res = add_mtd_partitions(&data->mtd, data->parts,
+			pdata->chip.nr_partitions);
+	} else
+#endif
+	res = add_mtd_device(&data->mtd);
+
+	if (!res)
+		return res;
+
+	nand_release(&data->mtd);
+out:
+	platform_set_drvdata(pdev, NULL);
+	iounmap(data->io_base);
+	kfree(data);
+	return res;
+}
+
+/*
+ * Remove a NAND device.
+ */
+static int __devexit plat_nand_remove(struct platform_device *pdev)
+{
+	struct plat_nand_data *data = platform_get_drvdata(pdev);
+	struct platform_nand_data *pdata = pdev->dev.platform_data;
+
+	nand_release(&data->mtd);
+#ifdef CONFIG_MTD_PARTITIONS
+	if (data->parts && data->parts != pdata->chip.partitions)
+		kfree(data->parts);
+#endif
+	iounmap(data->io_base);
+	kfree(data);
+
+	return 0;
+}
+
+static struct platform_driver plat_nand_driver = {
+	.probe		= plat_nand_probe,
+	.remove		= plat_nand_remove,
+	.driver		= {
+		.name	= "gen_nand",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init plat_nand_init(void)
+{
+	return platform_driver_register(&plat_nand_driver);
+}
+
+static void __exit plat_nand_exit(void)
+{
+	platform_driver_unregister(&plat_nand_driver);
+}
+
+module_init(plat_nand_init);
+module_exit(plat_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vitaly Wool");
+MODULE_DESCRIPTION("Simple generic NAND driver");


From linux-mtd at lists.infradead.org  Wed May  9 04:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 09 May 2007 04:59:01 -0400
Subject: [MTD] [MAPS] Fix missing printk() parameter in physmap_of.c MTD driver
Message-ID: <E1Hli0j-0003Oc-TA@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=2d0fa586facb740b9ef9a01dcedc94c126c6f148
Commit:     2d0fa586facb740b9ef9a01dcedc94c126c6f148
Parent:     711fdf627ce1374796632f16acec1ab63d11e38f
Author:     David Gibson <david at gibson.dropbear.id.au>
AuthorDate: Wed May 9 11:55:51 2007 +1000
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 9 09:38:48 2007 +0100

    [MTD] [MAPS] Fix missing printk() parameter in physmap_of.c MTD driver
    
    Squashes a compiler warning, and provides more useful information in
    the case messed up device tree information.
    
    Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/maps/physmap_of.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index 7efe744..8ec2fbe 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -186,7 +186,7 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev
 	else {
  		if (strcmp(of_probe, "ROM"))
 			dev_dbg(&dev->dev, "map_probe: don't know probe type "
-			"'%s', mapping as rom\n");
+			"'%s', mapping as rom\n", of_probe);
 		info->mtd = do_map_probe("mtd_rom", &info->map);
 	}
 	if (info->mtd == NULL) {


From linux-mtd at lists.infradead.org  Wed May  9 04:59:02 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 09 May 2007 04:59:02 -0400
Subject: [MTD] [MAPS] Remove flash maps for no longer supported 405LP boards
Message-ID: <E1Hli0j-0003Ow-W9@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=025257c7a7665a117b72097cc12021140eac8a34
Commit:     025257c7a7665a117b72097cc12021140eac8a34
Parent:     2d0fa586facb740b9ef9a01dcedc94c126c6f148
Author:     David Gibson <david at gibson.dropbear.id.au>
AuthorDate: Wed May 9 17:13:31 2007 +1000
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 9 09:43:02 2007 +0100

    [MTD] [MAPS] Remove flash maps for no longer supported 405LP boards
    
    drivers/mtd/maps includes flash maps for the Beech and Arctic PowerPC
    405LP based boards.  However, the 405LP was discontinued before any
    quantity were distributed and those boards no longer have kernel
    support in general.  Therefore, this patch removes this obsolete code.
    
    Signed-off-by: David Gibson <dwg at au1.ibm.com>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/maps/Kconfig      |   16 -----
 drivers/mtd/maps/Makefile     |    2 -
 drivers/mtd/maps/arctic-mtd.c |  145 -----------------------------------------
 drivers/mtd/maps/beech-mtd.c  |  122 ----------------------------------
 4 files changed, 0 insertions(+), 285 deletions(-)

diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index d990d81..aab5506 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -358,22 +358,6 @@ config MTD_CFI_FLAGADM
 	  Mapping for the Flaga digital module. If you don't have one, ignore
 	  this setting.
 
-config MTD_BEECH
-	tristate "CFI Flash device mapped on IBM 405LP Beech"
-	depends on MTD_CFI && BEECH
-	help
-	  This enables access routines for the flash chips on the IBM
-	  405LP Beech board. If you have one of these boards and would like
-	  to use the flash chips on it, say 'Y'.
-
-config MTD_ARCTIC
-	tristate "CFI Flash device mapped on IBM 405LP Arctic"
-	depends on MTD_CFI && ARCTIC2
-	help
-	  This enables access routines for the flash chips on the IBM 405LP
-	  Arctic board. If you have one of these boards and would like to
-	  use the flash chips on it, say 'Y'.
-
 config MTD_WALNUT
 	tristate "Flash device mapped on IBM 405GP Walnut"
 	depends on MTD_JEDECPROBE && WALNUT
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index de036c5..3acbb5d 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -58,8 +58,6 @@ obj-$(CONFIG_MTD_NETtel)	+= nettel.o
 obj-$(CONFIG_MTD_SCB2_FLASH)	+= scb2_flash.o
 obj-$(CONFIG_MTD_EBONY)		+= ebony.o
 obj-$(CONFIG_MTD_OCOTEA)	+= ocotea.o
-obj-$(CONFIG_MTD_BEECH)		+= beech-mtd.o
-obj-$(CONFIG_MTD_ARCTIC)	+= arctic-mtd.o
 obj-$(CONFIG_MTD_WALNUT)        += walnut.o
 obj-$(CONFIG_MTD_H720X)		+= h720x-flash.o
 obj-$(CONFIG_MTD_SBC8240)	+= sbc8240.o
diff --git a/drivers/mtd/maps/arctic-mtd.c b/drivers/mtd/maps/arctic-mtd.c
deleted file mode 100644
index 2cc9024..0000000
--- a/drivers/mtd/maps/arctic-mtd.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * $Id: arctic-mtd.c,v 1.14 2005/11/07 11:14:26 gleixner Exp $
- *
- * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for
- *                              IBM 405LP Arctic boards.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Copyright (C) 2002, International Business Machines Corporation
- * All Rights Reserved.
- *
- * Bishop Brock
- * IBM Research, Austin Center for Low-Power Computing
- * bcbrock at us.ibm.com
- * March 2002
- *
- * modified for Arctic by,
- * David Gibson
- * IBM OzLabs, Canberra, Australia
- * <arctic at gibson.dropbear.id.au>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/init.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/ibm4xx.h>
-
-/*
- * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB)
- * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB)
- * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP)
- * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB)
- */
-
-#define FFS1_SIZE	0x01000000 /* 16MiB */
-#define KERNEL_SIZE	0x00500000 /* 5.12MiB */
-#define FFS2_SIZE	0x00a60000 /* 10.624MiB */
-#define FIRMWARE_SIZE	0x000a0000 /* 640KiB */
-
-
-#define NAME     	"Arctic Linux Flash"
-#define PADDR    	SUBZERO_BOOTFLASH_PADDR
-#define BUSWIDTH	2
-#define SIZE		SUBZERO_BOOTFLASH_SIZE
-#define PARTITIONS	4
-
-/* Flash memories on these boards are memory resources, accessed big-endian. */
-
-{
-  /* do nothing for now */
-}
-
-static struct map_info arctic_mtd_map = {
-	.name		= NAME,
-	.size		= SIZE,
-	.bankwidth	= BUSWIDTH,
-	.phys		= PADDR,
-};
-
-static struct mtd_info *arctic_mtd;
-
-static struct mtd_partition arctic_partitions[PARTITIONS] = {
-	{ .name		= "Filesystem",
-	  .size		= FFS1_SIZE,
-	  .offset	= 0,},
-        { .name		= "Kernel",
-	  .size		= KERNEL_SIZE,
-	  .offset	= FFS1_SIZE,},
-	{ .name		= "Filesystem",
-	  .size		= FFS2_SIZE,
-	  .offset	= FFS1_SIZE + KERNEL_SIZE,},
-	{ .name		= "Firmware",
-	  .size		= FIRMWARE_SIZE,
-	  .offset	= SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,},
-};
-
-static int __init
-init_arctic_mtd(void)
-{
-	int err;
-
-	printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
-
-	arctic_mtd_map.virt = ioremap(PADDR, SIZE);
-
-	if (!arctic_mtd_map.virt) {
-		printk("%s: failed to ioremap 0x%x\n", NAME, PADDR);
-		return -EIO;
-	}
-	simple_map_init(&arctic_mtd_map);
-
-	printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8);
-	arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map);
-
-	if (!arctic_mtd) {
-		iounmap(arctic_mtd_map.virt);
-		return -ENXIO;
-	}
-
-	arctic_mtd->owner = THIS_MODULE;
-
-	err = add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS);
-	if (err) {
-		printk("%s: add_mtd_partitions failed\n", NAME);
-		iounmap(arctic_mtd_map.virt);
-	}
-
-	return err;
-}
-
-static void __exit
-cleanup_arctic_mtd(void)
-{
-	if (arctic_mtd) {
-		del_mtd_partitions(arctic_mtd);
-		map_destroy(arctic_mtd);
-		iounmap((void *) arctic_mtd_map.virt);
-	}
-}
-
-module_init(init_arctic_mtd);
-module_exit(cleanup_arctic_mtd);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Gibson <arctic at gibson.dropbear.id.au>");
-MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards");
diff --git a/drivers/mtd/maps/beech-mtd.c b/drivers/mtd/maps/beech-mtd.c
deleted file mode 100644
index d76d598..0000000
--- a/drivers/mtd/maps/beech-mtd.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * $Id: beech-mtd.c,v 1.11 2005/11/07 11:14:26 gleixner Exp $
- *
- * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for
- *                              IBM 405LP Beech boards.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Copyright (C) 2002, International Business Machines Corporation
- * All Rights Reserved.
- *
- * Bishop Brock
- * IBM Research, Austin Center for Low-Power Computing
- * bcbrock at us.ibm.com
- * March 2002
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/init.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/ibm4xx.h>
-
-#define NAME     "Beech Linux Flash"
-#define PADDR    BEECH_BIGFLASH_PADDR
-#define SIZE     BEECH_BIGFLASH_SIZE
-#define BUSWIDTH 1
-
-/* Flash memories on these boards are memory resources, accessed big-endian. */
-
-
-static struct map_info beech_mtd_map = {
-	.name =		NAME,
-	.size =		SIZE,
-	.bankwidth =	BUSWIDTH,
-	.phys =		PADDR
-};
-
-static struct mtd_info *beech_mtd;
-
-static struct mtd_partition beech_partitions[2] = {
-	{
-	      .name = "Linux Kernel",
-	      .size = BEECH_KERNEL_SIZE,
-	      .offset = BEECH_KERNEL_OFFSET
-	}, {
-	      .name = "Free Area",
-	      .size = BEECH_FREE_AREA_SIZE,
-	      .offset = BEECH_FREE_AREA_OFFSET
-	}
-};
-
-static int __init
-init_beech_mtd(void)
-{
-	int err;
-
-	printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
-
-	beech_mtd_map.virt = ioremap(PADDR, SIZE);
-
-	if (!beech_mtd_map.virt) {
-		printk("%s: failed to ioremap 0x%x\n", NAME, PADDR);
-		return -EIO;
-	}
-
-	simple_map_init(&beech_mtd_map);
-
-	printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8);
-	beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map);
-
-	if (!beech_mtd) {
-		iounmap(beech_mtd_map.virt);
-		return -ENXIO;
-	}
-
-	beech_mtd->owner = THIS_MODULE;
-
-	err = add_mtd_partitions(beech_mtd, beech_partitions, 2);
-	if (err) {
-		printk("%s: add_mtd_partitions failed\n", NAME);
-		iounmap(beech_mtd_map.virt);
-	}
-
-	return err;
-}
-
-static void __exit
-cleanup_beech_mtd(void)
-{
-	if (beech_mtd) {
-		del_mtd_partitions(beech_mtd);
-		map_destroy(beech_mtd);
-		iounmap((void *) beech_mtd_map.virt);
-	}
-}
-
-module_init(init_beech_mtd);
-module_exit(cleanup_beech_mtd);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Bishop Brock <bcbrock at us.ibm.com>");
-MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards");


From linux-mtd at lists.infradead.org  Wed May  9 08:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 09 May 2007 08:59:01 -0400
Subject: [MTD] Remove unnecessary user space check from mtd.h.
Message-ID: <E1Hllkz-0003HS-4d@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=36200b76008d52d16b170d4f7dae9cfe00f5eb2b
Commit:     36200b76008d52d16b170d4f7dae9cfe00f5eb2b
Parent:     025257c7a7665a117b72097cc12021140eac8a34
Author:     Robert P. J. Day <rpjday at mindspring.com>
AuthorDate: Thu May 3 15:58:49 2007 -0400
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 9 13:24:37 2007 +0100

    [MTD] Remove unnecessary user space check from mtd.h.
    
    Since the header file include/linux/mtd/mtd.h is not exported to user
    space, remove the user space check and error.
    
    Signed-off-by: Robert P. J. Day <rpjday at mindspring.com>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 include/linux/mtd/mtd.h |    4 ----
 1 files changed, 0 insertions(+), 4 deletions(-)

diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 45d482c..12a9a18 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -9,10 +9,6 @@
 #ifndef __MTD_MTD_H__
 #define __MTD_MTD_H__
 
-#ifndef __KERNEL__
-#error This is a kernel header. Perhaps include mtd-user.h instead?
-#endif
-
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/uio.h>


From linux-mtd at lists.infradead.org  Wed May  9 08:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 09 May 2007 08:59:01 -0400
Subject: [MTD] Delete allegedly obsolete "bank_size" field of mtd_info.
Message-ID: <E1Hllkz-0003Hn-8U@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=42f209d3c94516affeb5e578fae62925f531a2d9
Commit:     42f209d3c94516affeb5e578fae62925f531a2d9
Parent:     36200b76008d52d16b170d4f7dae9cfe00f5eb2b
Author:     Robert P. J. Day <rpjday at mindspring.com>
AuthorDate: Fri May 4 15:49:38 2007 -0400
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 9 13:26:52 2007 +0100

    [MTD] Delete allegedly obsolete "bank_size" field of mtd_info.
    
    Delete the allegedly obsolete "bank_size" member of struct mtd_info.
    
    Signed-off-by: Robert P. J. Day <rpjday at mindspring.com>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/mtdpart.c   |    1 -
 include/linux/mtd/mtd.h |    3 ---
 2 files changed, 0 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 1af9890..9c62368 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -347,7 +347,6 @@ int add_mtd_partitions(struct mtd_info *master,
 		slave->mtd.subpage_sft = master->subpage_sft;
 
 		slave->mtd.name = parts[i].name;
-		slave->mtd.bank_size = master->bank_size;
 		slave->mtd.owner = master->owner;
 
 		slave->mtd.read = part_read;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 12a9a18..fd64ccf 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -133,9 +133,6 @@ struct mtd_info {
 	int numeraseregions;
 	struct mtd_erase_region_info *eraseregions;
 
-	/* This really shouldn't be here. It can go away in 2.5 */
-	u_int32_t bank_size;
-
 	int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
 
 	/* This stuff for eXecute-In-Place */


From linux-mtd at lists.infradead.org  Wed May  9 08:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Wed, 09 May 2007 08:59:01 -0400
Subject: [MTD] [CHIPS] Remove MTD_OBSOLETE_CHIPS (jedec, amd_flash, sharp)
Message-ID: <E1Hllkz-0003I8-Af@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=b7aa48be1e7a11e36448a7db58931bbf735d2718
Commit:     b7aa48be1e7a11e36448a7db58931bbf735d2718
Parent:     42f209d3c94516affeb5e578fae62925f531a2d9
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Wed May 9 13:34:37 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed May 9 13:34:37 2007 +0100

    [MTD] [CHIPS] Remove MTD_OBSOLETE_CHIPS (jedec, amd_flash, sharp)
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/chips/Kconfig     |   40 --
 drivers/mtd/chips/Makefile    |    4 -
 drivers/mtd/chips/amd_flash.c | 1396 -----------------------------------------
 drivers/mtd/chips/jedec.c     |  935 ---------------------------
 drivers/mtd/chips/sharp.c     |  601 ------------------
 5 files changed, 0 insertions(+), 2976 deletions(-)

diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index d28e0fc..479d32b 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -1,5 +1,4 @@
 # drivers/mtd/chips/Kconfig
-# $Id: Kconfig,v 1.18 2005/11/07 11:14:22 gleixner Exp $
 
 menu "RAM/ROM/Flash chip drivers"
 	depends on MTD!=n
@@ -231,45 +230,6 @@ config MTD_ABSENT
 	  the system regardless of media presence.  Device nodes created
 	  with this driver will return -ENODEV upon access.
 
-config MTD_OBSOLETE_CHIPS
-	bool "Older (theoretically obsoleted now) drivers for non-CFI chips"
-	help
-	  This option does not enable any code directly, but will allow you to
-	  select some other chip drivers which are now considered obsolete,
-	  because the generic CONFIG_JEDECPROBE code above should now detect
-	  the chips which are supported by these drivers, and allow the generic
-	  CFI-compatible drivers to drive the chips. Say 'N' here unless you have
-	  already tried the CONFIG_JEDECPROBE method and reported its failure
-	  to the MTD mailing list at <linux-mtd at lists.infradead.org>
-
-config MTD_AMDSTD
-	tristate "AMD compatible flash chip support (non-CFI)"
-	depends on MTD_OBSOLETE_CHIPS && BROKEN
-	help
-	  This option enables support for flash chips using AMD-compatible
-	  commands, including some which are not CFI-compatible and hence
-	  cannot be used with the CONFIG_MTD_CFI_AMDSTD option.
-
-	  It also works on AMD compatible chips that do conform to CFI.
-
-config MTD_SHARP
-	tristate "pre-CFI Sharp chip support"
-	depends on MTD_OBSOLETE_CHIPS
-	help
-	  This option enables support for flash chips using Sharp-compatible
-	  commands, including some which are not CFI-compatible and hence
-	  cannot be used with the CONFIG_MTD_CFI_INTELxxx options.
-
-config MTD_JEDEC
-	tristate "JEDEC device support"
-	depends on MTD_OBSOLETE_CHIPS && BROKEN
-	help
-	  Enable older JEDEC flash interface devices for self
-	  programming flash.  It is commonly used in older AMD chips.  It is
-	  only called JEDEC because the JEDEC association
-	  <http://www.jedec.org/> distributes the identification codes for the
-	  chips.
-
 config MTD_XIP
 	bool "XIP aware MTD support"
 	depends on !SMP && (MTD_CFI_INTELEXT || MTD_CFI_AMDSTD) && EXPERIMENTAL && ARCH_MTD_XIP
diff --git a/drivers/mtd/chips/Makefile b/drivers/mtd/chips/Makefile
index 75bc1c2..3658241 100644
--- a/drivers/mtd/chips/Makefile
+++ b/drivers/mtd/chips/Makefile
@@ -1,19 +1,15 @@
 #
 # linux/drivers/chips/Makefile
 #
-# $Id: Makefile.common,v 1.5 2005/11/07 11:14:22 gleixner Exp $
 
 obj-$(CONFIG_MTD)		+= chipreg.o
-obj-$(CONFIG_MTD_AMDSTD)	+= amd_flash.o
 obj-$(CONFIG_MTD_CFI)		+= cfi_probe.o
 obj-$(CONFIG_MTD_CFI_UTIL)	+= cfi_util.o
 obj-$(CONFIG_MTD_CFI_STAA)	+= cfi_cmdset_0020.o
 obj-$(CONFIG_MTD_CFI_AMDSTD)	+= cfi_cmdset_0002.o
 obj-$(CONFIG_MTD_CFI_INTELEXT)	+= cfi_cmdset_0001.o
 obj-$(CONFIG_MTD_GEN_PROBE)	+= gen_probe.o
-obj-$(CONFIG_MTD_JEDEC)		+= jedec.o
 obj-$(CONFIG_MTD_JEDECPROBE)	+= jedec_probe.o
 obj-$(CONFIG_MTD_RAM)		+= map_ram.o
 obj-$(CONFIG_MTD_ROM)		+= map_rom.o
-obj-$(CONFIG_MTD_SHARP)		+= sharp.o
 obj-$(CONFIG_MTD_ABSENT)	+= map_absent.o
diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c
deleted file mode 100644
index e7999f1..0000000
--- a/drivers/mtd/chips/amd_flash.c
+++ /dev/null
@@ -1,1396 +0,0 @@
-/*
- * MTD map driver for AMD compatible flash chips (non-CFI)
- *
- * Author: Jonas Holmberg <jonas.holmberg at axis.com>
- *
- * $Id: amd_flash.c,v 1.28 2005/11/07 11:14:22 gleixner Exp $
- *
- * Copyright (c) 2001 Axis Communications AB
- *
- * This file is under GPL.
- *
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/flashchip.h>
-
-/* There's no limit. It exists only to avoid realloc. */
-#define MAX_AMD_CHIPS 8
-
-#define DEVICE_TYPE_X8	(8 / 8)
-#define DEVICE_TYPE_X16	(16 / 8)
-#define DEVICE_TYPE_X32	(32 / 8)
-
-/* Addresses */
-#define ADDR_MANUFACTURER		0x0000
-#define ADDR_DEVICE_ID			0x0001
-#define ADDR_SECTOR_LOCK		0x0002
-#define ADDR_HANDSHAKE			0x0003
-#define ADDR_UNLOCK_1			0x0555
-#define ADDR_UNLOCK_2			0x02AA
-
-/* Commands */
-#define CMD_UNLOCK_DATA_1		0x00AA
-#define CMD_UNLOCK_DATA_2		0x0055
-#define CMD_MANUFACTURER_UNLOCK_DATA	0x0090
-#define CMD_UNLOCK_BYPASS_MODE		0x0020
-#define CMD_PROGRAM_UNLOCK_DATA		0x00A0
-#define CMD_RESET_DATA			0x00F0
-#define CMD_SECTOR_ERASE_UNLOCK_DATA	0x0080
-#define CMD_SECTOR_ERASE_UNLOCK_DATA_2	0x0030
-
-#define CMD_UNLOCK_SECTOR		0x0060
-
-/* Manufacturers */
-#define MANUFACTURER_AMD	0x0001
-#define MANUFACTURER_ATMEL	0x001F
-#define MANUFACTURER_FUJITSU	0x0004
-#define MANUFACTURER_ST		0x0020
-#define MANUFACTURER_SST	0x00BF
-#define MANUFACTURER_TOSHIBA	0x0098
-
-/* AMD */
-#define AM29F800BB	0x2258
-#define AM29F800BT	0x22D6
-#define AM29LV800BB	0x225B
-#define AM29LV800BT	0x22DA
-#define AM29LV160DT	0x22C4
-#define AM29LV160DB	0x2249
-#define AM29BDS323D     0x22D1
-
-/* Atmel */
-#define AT49xV16x	0x00C0
-#define AT49xV16xT	0x00C2
-
-/* Fujitsu */
-#define MBM29LV160TE	0x22C4
-#define MBM29LV160BE	0x2249
-#define MBM29LV800BB	0x225B
-
-/* ST - www.st.com */
-#define M29W800T	0x00D7
-#define M29W160DT	0x22C4
-#define M29W160DB	0x2249
-
-/* SST */
-#define SST39LF800	0x2781
-#define SST39LF160	0x2782
-
-/* Toshiba */
-#define TC58FVT160	0x00C2
-#define TC58FVB160	0x0043
-
-#define D6_MASK	0x40
-
-struct amd_flash_private {
-	int device_type;
-	int interleave;
-	int numchips;
-	unsigned long chipshift;
-	struct flchip chips[0];
-};
-
-struct amd_flash_info {
-	const __u16 mfr_id;
-	const __u16 dev_id;
-	const char *name;
-	const u_long size;
-	const int numeraseregions;
-	const struct mtd_erase_region_info regions[4];
-};
-
-
-
-static int amd_flash_read(struct mtd_info *, loff_t, size_t, size_t *,
-			  u_char *);
-static int amd_flash_write(struct mtd_info *, loff_t, size_t, size_t *,
-			   const u_char *);
-static int amd_flash_erase(struct mtd_info *, struct erase_info *);
-static void amd_flash_sync(struct mtd_info *);
-static int amd_flash_suspend(struct mtd_info *);
-static void amd_flash_resume(struct mtd_info *);
-static void amd_flash_destroy(struct mtd_info *);
-static struct mtd_info *amd_flash_probe(struct map_info *map);
-
-
-static struct mtd_chip_driver amd_flash_chipdrv = {
-	.probe = amd_flash_probe,
-	.destroy = amd_flash_destroy,
-	.name = "amd_flash",
-	.module = THIS_MODULE
-};
-
-static inline __u32 wide_read(struct map_info *map, __u32 addr)
-{
-	if (map->buswidth == 1) {
-		return map_read8(map, addr);
-	} else if (map->buswidth == 2) {
-		return map_read16(map, addr);
-	} else if (map->buswidth == 4) {
-		return map_read32(map, addr);
-        }
-
-	return 0;
-}
-
-static inline void wide_write(struct map_info *map, __u32 val, __u32 addr)
-{
-	if (map->buswidth == 1) {
-		map_write8(map, val, addr);
-	} else if (map->buswidth == 2) {
-		map_write16(map, val, addr);
-	} else if (map->buswidth == 4) {
-		map_write32(map, val, addr);
-	}
-}
-
-static inline __u32 make_cmd(struct map_info *map, __u32 cmd)
-{
-	const struct amd_flash_private *private = map->fldrv_priv;
-	if ((private->interleave == 2) &&
-	    (private->device_type == DEVICE_TYPE_X16)) {
-		cmd |= (cmd << 16);
-	}
-
-	return cmd;
-}
-
-static inline void send_unlock(struct map_info *map, unsigned long base)
-{
-	wide_write(map, (CMD_UNLOCK_DATA_1 << 16) | CMD_UNLOCK_DATA_1,
-		   base + (map->buswidth * ADDR_UNLOCK_1));
-	wide_write(map, (CMD_UNLOCK_DATA_2 << 16) | CMD_UNLOCK_DATA_2,
-		   base + (map->buswidth * ADDR_UNLOCK_2));
-}
-
-static inline void send_cmd(struct map_info *map, unsigned long base, __u32 cmd)
-{
-	send_unlock(map, base);
-	wide_write(map, make_cmd(map, cmd),
-		   base + (map->buswidth * ADDR_UNLOCK_1));
-}
-
-static inline void send_cmd_to_addr(struct map_info *map, unsigned long base,
-				    __u32 cmd, unsigned long addr)
-{
-	send_unlock(map, base);
-	wide_write(map, make_cmd(map, cmd), addr);
-}
-
-static inline int flash_is_busy(struct map_info *map, unsigned long addr,
-				int interleave)
-{
-
-	if ((interleave == 2) && (map->buswidth == 4)) {
-		__u32 read1, read2;
-
-		read1 = wide_read(map, addr);
-		read2 = wide_read(map, addr);
-
-		return (((read1 >> 16) & D6_MASK) !=
-			((read2 >> 16) & D6_MASK)) ||
-		       (((read1 & 0xffff) & D6_MASK) !=
-			((read2 & 0xffff) & D6_MASK));
-	}
-
-	return ((wide_read(map, addr) & D6_MASK) !=
-		(wide_read(map, addr) & D6_MASK));
-}
-
-static inline void unlock_sector(struct map_info *map, unsigned long sect_addr,
-				 int unlock)
-{
-	/* Sector lock address. A6 = 1 for unlock, A6 = 0 for lock */
-	int SLA = unlock ?
-		(sect_addr |  (0x40 * map->buswidth)) :
-		(sect_addr & ~(0x40 * map->buswidth)) ;
-
-	__u32 cmd = make_cmd(map, CMD_UNLOCK_SECTOR);
-
-	wide_write(map, make_cmd(map, CMD_RESET_DATA), 0);
-	wide_write(map, cmd, SLA); /* 1st cycle: write cmd to any address */
-	wide_write(map, cmd, SLA); /* 2nd cycle: write cmd to any address */
-	wide_write(map, cmd, SLA); /* 3rd cycle: write cmd to SLA */
-}
-
-static inline int is_sector_locked(struct map_info *map,
-				   unsigned long sect_addr)
-{
-	int status;
-
-	wide_write(map, CMD_RESET_DATA, 0);
-	send_cmd(map, sect_addr, CMD_MANUFACTURER_UNLOCK_DATA);
-
-	/* status is 0x0000 for unlocked and 0x0001 for locked */
-	status = wide_read(map, sect_addr + (map->buswidth * ADDR_SECTOR_LOCK));
-	wide_write(map, CMD_RESET_DATA, 0);
-	return status;
-}
-
-static int amd_flash_do_unlock(struct mtd_info *mtd, loff_t ofs, size_t len,
-			       int is_unlock)
-{
-	struct map_info *map;
-	struct mtd_erase_region_info *merip;
-	int eraseoffset, erasesize, eraseblocks;
-	int i;
-	int retval = 0;
-	int lock_status;
-
-	map = mtd->priv;
-
-	/* Pass the whole chip through sector by sector and check for each
-	   sector if the sector and the given interval overlap */
-	for(i = 0; i < mtd->numeraseregions; i++) {
-		merip = &mtd->eraseregions[i];
-
-		eraseoffset = merip->offset;
-		erasesize = merip->erasesize;
-		eraseblocks = merip->numblocks;
-
-		if (ofs > eraseoffset + erasesize)
-			continue;
-
-		while (eraseblocks > 0) {
-			if (ofs < eraseoffset + erasesize && ofs + len > eraseoffset) {
-				unlock_sector(map, eraseoffset, is_unlock);
-
-				lock_status = is_sector_locked(map, eraseoffset);
-
-				if (is_unlock && lock_status) {
-					printk("Cannot unlock sector at address %x length %xx\n",
-					       eraseoffset, merip->erasesize);
-					retval = -1;
-				} else if (!is_unlock && !lock_status) {
-					printk("Cannot lock sector at address %x length %x\n",
-					       eraseoffset, merip->erasesize);
-					retval = -1;
-				}
-			}
-			eraseoffset += erasesize;
-			eraseblocks --;
-		}
-	}
-	return retval;
-}
-
-static int amd_flash_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
-{
-	return amd_flash_do_unlock(mtd, ofs, len, 1);
-}
-
-static int amd_flash_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
-{
-	return amd_flash_do_unlock(mtd, ofs, len, 0);
-}
-
-
-/*
- * Reads JEDEC manufacturer ID and device ID and returns the index of the first
- * matching table entry (-1 if not found or alias for already found chip).
- */
-static int probe_new_chip(struct mtd_info *mtd, __u32 base,
-			  struct flchip *chips,
-			  struct amd_flash_private *private,
-			  const struct amd_flash_info *table, int table_size)
-{
-	__u32 mfr_id;
-	__u32 dev_id;
-	struct map_info *map = mtd->priv;
-	struct amd_flash_private temp;
-	int i;
-
-	temp.device_type = DEVICE_TYPE_X16;	// Assume X16 (FIXME)
-	temp.interleave = 2;
-	map->fldrv_priv = &temp;
-
-	/* Enter autoselect mode. */
-	send_cmd(map, base, CMD_RESET_DATA);
-	send_cmd(map, base, CMD_MANUFACTURER_UNLOCK_DATA);
-
-	mfr_id = wide_read(map, base + (map->buswidth * ADDR_MANUFACTURER));
-	dev_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_ID));
-
-	if ((map->buswidth == 4) && ((mfr_id >> 16) == (mfr_id & 0xffff)) &&
-	    ((dev_id >> 16) == (dev_id & 0xffff))) {
-		mfr_id &= 0xffff;
-		dev_id &= 0xffff;
-	} else {
-		temp.interleave = 1;
-	}
-
-	for (i = 0; i < table_size; i++) {
-		if ((mfr_id == table[i].mfr_id) &&
-		    (dev_id == table[i].dev_id)) {
-			if (chips) {
-				int j;
-
-				/* Is this an alias for an already found chip?
-				 * In that case that chip should be in
-				 * autoselect mode now.
-				 */
-				for (j = 0; j < private->numchips; j++) {
-					__u32 mfr_id_other;
-					__u32 dev_id_other;
-
-					mfr_id_other =
-						wide_read(map, chips[j].start +
-							       (map->buswidth *
-								ADDR_MANUFACTURER
-							       ));
-					dev_id_other =
-						wide_read(map, chips[j].start +
-					    		       (map->buswidth *
-							        ADDR_DEVICE_ID));
-					if (temp.interleave == 2) {
-						mfr_id_other &= 0xffff;
-						dev_id_other &= 0xffff;
-					}
-					if ((mfr_id_other == mfr_id) &&
-					    (dev_id_other == dev_id)) {
-
-						/* Exit autoselect mode. */
-						send_cmd(map, base,
-							 CMD_RESET_DATA);
-
-						return -1;
-					}
-				}
-
-				if (private->numchips == MAX_AMD_CHIPS) {
-					printk(KERN_WARNING
-					       "%s: Too many flash chips "
-					       "detected. Increase "
-					       "MAX_AMD_CHIPS from %d.\n",
-					       map->name, MAX_AMD_CHIPS);
-
-					return -1;
-				}
-
-				chips[private->numchips].start = base;
-				chips[private->numchips].state = FL_READY;
-				chips[private->numchips].mutex =
-					&chips[private->numchips]._spinlock;
-				private->numchips++;
-			}
-
-			printk("%s: Found %d x %ldMiB %s at 0x%x\n", map->name,
-			       temp.interleave, (table[i].size)/(1024*1024),
-			       table[i].name, base);
-
-			mtd->size += table[i].size * temp.interleave;
-			mtd->numeraseregions += table[i].numeraseregions;
-
-			break;
-		}
-	}
-
-	/* Exit autoselect mode. */
-	send_cmd(map, base, CMD_RESET_DATA);
-
-	if (i == table_size) {
-		printk(KERN_DEBUG "%s: unknown flash device at 0x%x, "
-		       "mfr id 0x%x, dev id 0x%x\n", map->name,
-		       base, mfr_id, dev_id);
-		map->fldrv_priv = NULL;
-
-		return -1;
-	}
-
-	private->device_type = temp.device_type;
-	private->interleave = temp.interleave;
-
-	return i;
-}
-
-
-
-static struct mtd_info *amd_flash_probe(struct map_info *map)
-{
-	static const struct amd_flash_info table[] = {
-	{
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29LV160DT,
-		.name = "AMD AM29LV160DT",
-		.size = 0x00200000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
-			{ .offset = 0x1F0000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x1F8000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x1FC000, .erasesize = 0x04000, .numblocks =  1 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29LV160DB,
-		.name = "AMD AM29LV160DB",
-		.size = 0x00200000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
-			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_TOSHIBA,
-		.dev_id = TC58FVT160,
-		.name = "Toshiba TC58FVT160",
-		.size = 0x00200000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
-			{ .offset = 0x1F0000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x1F8000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x1FC000, .erasesize = 0x04000, .numblocks =  1 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_FUJITSU,
-		.dev_id = MBM29LV160TE,
-		.name = "Fujitsu MBM29LV160TE",
-		.size = 0x00200000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
-			{ .offset = 0x1F0000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x1F8000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x1FC000, .erasesize = 0x04000, .numblocks =  1 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_TOSHIBA,
-		.dev_id = TC58FVB160,
-		.name = "Toshiba TC58FVB160",
-		.size = 0x00200000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
-			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_FUJITSU,
-		.dev_id = MBM29LV160BE,
-		.name = "Fujitsu MBM29LV160BE",
-		.size = 0x00200000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
-			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29LV800BB,
-		.name = "AMD AM29LV800BB",
-		.size = 0x00100000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
-			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29F800BB,
-		.name = "AMD AM29F800BB",
-		.size = 0x00100000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
-			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29LV800BT,
-		.name = "AMD AM29LV800BT",
-		.size = 0x00100000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
-			{ .offset = 0x0F0000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x0F8000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x0FC000, .erasesize = 0x04000, .numblocks =  1 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29F800BT,
-		.name = "AMD AM29F800BT",
-		.size = 0x00100000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
-			{ .offset = 0x0F0000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x0F8000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x0FC000, .erasesize = 0x04000, .numblocks =  1 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29LV800BB,
-		.name = "AMD AM29LV800BB",
-		.size = 0x00100000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
-			{ .offset = 0x0F0000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x0F8000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x0FC000, .erasesize = 0x04000, .numblocks =  1 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_FUJITSU,
-		.dev_id = MBM29LV800BB,
-		.name = "Fujitsu MBM29LV800BB",
-		.size = 0x00100000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
-			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_ST,
-		.dev_id = M29W800T,
-		.name = "ST M29W800T",
-		.size = 0x00100000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
-			{ .offset = 0x0F0000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x0F8000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x0FC000, .erasesize = 0x04000, .numblocks =  1 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_ST,
-		.dev_id = M29W160DT,
-		.name = "ST M29W160DT",
-		.size = 0x00200000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
-			{ .offset = 0x1F0000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x1F8000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x1FC000, .erasesize = 0x04000, .numblocks =  1 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_ST,
-		.dev_id = M29W160DB,
-		.name = "ST M29W160DB",
-		.size = 0x00200000,
-		.numeraseregions = 4,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
-			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
-			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
-			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29BDS323D,
-		.name = "AMD AM29BDS323D",
-		.size = 0x00400000,
-		.numeraseregions = 3,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 },
-			{ .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 },
-			{ .offset = 0x3f0000, .erasesize = 0x02000, .numblocks =  8 },
-		}
-	}, {
-		.mfr_id = MANUFACTURER_ATMEL,
-		.dev_id = AT49xV16x,
-		.name = "Atmel AT49xV16x",
-		.size = 0x00200000,
-		.numeraseregions = 2,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x02000, .numblocks =  8 },
-			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
-		}
-	}, {
-		.mfr_id = MANUFACTURER_ATMEL,
-		.dev_id = AT49xV16xT,
-		.name = "Atmel AT49xV16xT",
-		.size = 0x00200000,
-		.numeraseregions = 2,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
-			{ .offset = 0x1F0000, .erasesize = 0x02000, .numblocks =  8 }
-		}
-	}
-	};
-
-	struct mtd_info *mtd;
-	struct flchip chips[MAX_AMD_CHIPS];
-	int table_pos[MAX_AMD_CHIPS];
-	struct amd_flash_private temp;
-	struct amd_flash_private *private;
-	u_long size;
-	unsigned long base;
-	int i;
-	int reg_idx;
-	int offset;
-
-	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
-	if (!mtd) {
-		printk(KERN_WARNING
-		       "%s: kmalloc failed for info structure\n", map->name);
-		return NULL;
-	}
-	mtd->priv = map;
-
-	memset(&temp, 0, sizeof(temp));
-
-	printk("%s: Probing for AMD compatible flash...\n", map->name);
-
-	if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table,
-					   ARRAY_SIZE(table)))
-	    == -1) {
-		printk(KERN_WARNING
-		       "%s: Found no AMD compatible device at location zero\n",
-		       map->name);
-		kfree(mtd);
-
-		return NULL;
-	}
-
-	chips[0].start = 0;
-	chips[0].state = FL_READY;
-	chips[0].mutex = &chips[0]._spinlock;
-	temp.numchips = 1;
-	for (size = mtd->size; size > 1; size >>= 1) {
-		temp.chipshift++;
-	}
-	switch (temp.interleave) {
-		case 2:
-			temp.chipshift += 1;
-			break;
-		case 4:
-			temp.chipshift += 2;
-			break;
-	}
-
-	/* Find out if there are any more chips in the map. */
-	for (base = (1 << temp.chipshift);
-	     base < map->size;
-	     base += (1 << temp.chipshift)) {
-	     	int numchips = temp.numchips;
-		table_pos[numchips] = probe_new_chip(mtd, base, chips,
-			&temp, table, ARRAY_SIZE(table));
-	}
-
-	mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) *
-				    mtd->numeraseregions, GFP_KERNEL);
-	if (!mtd->eraseregions) {
-		printk(KERN_WARNING "%s: Failed to allocate "
-		       "memory for MTD erase region info\n", map->name);
-		kfree(mtd);
-		map->fldrv_priv = NULL;
-		return NULL;
-	}
-
-	reg_idx = 0;
-	offset = 0;
-	for (i = 0; i < temp.numchips; i++) {
-		int dev_size;
-		int j;
-
-		dev_size = 0;
-		for (j = 0; j < table[table_pos[i]].numeraseregions; j++) {
-			mtd->eraseregions[reg_idx].offset = offset +
-				(table[table_pos[i]].regions[j].offset *
-				 temp.interleave);
-			mtd->eraseregions[reg_idx].erasesize =
-				table[table_pos[i]].regions[j].erasesize *
-				temp.interleave;
-			mtd->eraseregions[reg_idx].numblocks =
-				table[table_pos[i]].regions[j].numblocks;
-			if (mtd->erasesize <
-			    mtd->eraseregions[reg_idx].erasesize) {
-				mtd->erasesize =
-					mtd->eraseregions[reg_idx].erasesize;
-			}
-			dev_size += mtd->eraseregions[reg_idx].erasesize *
-				    mtd->eraseregions[reg_idx].numblocks;
-			reg_idx++;
-		}
-		offset += dev_size;
-	}
-	mtd->type = MTD_NORFLASH;
-	mtd->writesize = 1;
-	mtd->flags = MTD_CAP_NORFLASH;
-	mtd->name = map->name;
-	mtd->erase = amd_flash_erase;
-	mtd->read = amd_flash_read;
-	mtd->write = amd_flash_write;
-	mtd->sync = amd_flash_sync;
-	mtd->suspend = amd_flash_suspend;
-	mtd->resume = amd_flash_resume;
-	mtd->lock = amd_flash_lock;
-	mtd->unlock = amd_flash_unlock;
-
-	private = kmalloc(sizeof(*private) + (sizeof(struct flchip) *
-					      temp.numchips), GFP_KERNEL);
-	if (!private) {
-		printk(KERN_WARNING
-		       "%s: kmalloc failed for private structure\n", map->name);
-		kfree(mtd);
-		map->fldrv_priv = NULL;
-		return NULL;
-	}
-	memcpy(private, &temp, sizeof(temp));
-	memcpy(private->chips, chips,
-	       sizeof(struct flchip) * private->numchips);
-	for (i = 0; i < private->numchips; i++) {
-		init_waitqueue_head(&private->chips[i].wq);
-		spin_lock_init(&private->chips[i]._spinlock);
-	}
-
-	map->fldrv_priv = private;
-
-	map->fldrv = &amd_flash_chipdrv;
-
-	__module_get(THIS_MODULE);
-	return mtd;
-}
-
-
-
-static inline int read_one_chip(struct map_info *map, struct flchip *chip,
-			       loff_t adr, size_t len, u_char *buf)
-{
-	DECLARE_WAITQUEUE(wait, current);
-	unsigned long timeo = jiffies + HZ;
-
-retry:
-	spin_lock_bh(chip->mutex);
-
-	if (chip->state != FL_READY){
-		printk(KERN_INFO "%s: waiting for chip to read, state = %d\n",
-		       map->name, chip->state);
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-
-		spin_unlock_bh(chip->mutex);
-
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-
-		if(signal_pending(current)) {
-			return -EINTR;
-		}
-
-		timeo = jiffies + HZ;
-
-		goto retry;
-	}
-
-	adr += chip->start;
-
-	chip->state = FL_READY;
-
-	map_copy_from(map, buf, adr, len);
-
-	wake_up(&chip->wq);
-	spin_unlock_bh(chip->mutex);
-
-	return 0;
-}
-
-
-
-static int amd_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
-			  size_t *retlen, u_char *buf)
-{
-	struct map_info *map = mtd->priv;
-	struct amd_flash_private *private = map->fldrv_priv;
-	unsigned long ofs;
-	int chipnum;
-	int ret = 0;
-
-	if ((from + len) > mtd->size) {
-		printk(KERN_WARNING "%s: read request past end of device "
-		       "(0x%lx)\n", map->name, (unsigned long)from + len);
-
-		return -EINVAL;
-	}
-
-	/* Offset within the first chip that the first read should start. */
-	chipnum = (from >> private->chipshift);
-	ofs = from - (chipnum <<  private->chipshift);
-
-	*retlen = 0;
-
-	while (len) {
-		unsigned long this_len;
-
-		if (chipnum >= private->numchips) {
-			break;
-		}
-
-		if ((len + ofs - 1) >> private->chipshift) {
-			this_len = (1 << private->chipshift) - ofs;
-		} else {
-			this_len = len;
-		}
-
-		ret = read_one_chip(map, &private->chips[chipnum], ofs,
-				    this_len, buf);
-		if (ret) {
-			break;
-		}
-
-		*retlen += this_len;
-		len -= this_len;
-		buf += this_len;
-
-		ofs = 0;
-		chipnum++;
-	}
-
-	return ret;
-}
-
-
-
-static int write_one_word(struct map_info *map, struct flchip *chip,
-			  unsigned long adr, __u32 datum)
-{
-	unsigned long timeo = jiffies + HZ;
-	struct amd_flash_private *private = map->fldrv_priv;
-	DECLARE_WAITQUEUE(wait, current);
-	int ret = 0;
-	int times_left;
-
-retry:
-	spin_lock_bh(chip->mutex);
-
-	if (chip->state != FL_READY){
-		printk("%s: waiting for chip to write, state = %d\n",
-		       map->name, chip->state);
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-
-		spin_unlock_bh(chip->mutex);
-
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-		printk(KERN_INFO "%s: woke up to write\n", map->name);
-		if(signal_pending(current))
-			return -EINTR;
-
-		timeo = jiffies + HZ;
-
-		goto retry;
-	}
-
-	chip->state = FL_WRITING;
-
-	adr += chip->start;
-	ENABLE_VPP(map);
-	send_cmd(map, chip->start, CMD_PROGRAM_UNLOCK_DATA);
-	wide_write(map, datum, adr);
-
-	times_left = 500000;
-	while (times_left-- && flash_is_busy(map, adr, private->interleave)) {
-		if (need_resched()) {
-			spin_unlock_bh(chip->mutex);
-			schedule();
-			spin_lock_bh(chip->mutex);
-		}
-	}
-
-	if (!times_left) {
-		printk(KERN_WARNING "%s: write to 0x%lx timed out!\n",
-		       map->name, adr);
-		ret = -EIO;
-	} else {
-		__u32 verify;
-		if ((verify = wide_read(map, adr)) != datum) {
-			printk(KERN_WARNING "%s: write to 0x%lx failed. "
-			       "datum = %x, verify = %x\n",
-			       map->name, adr, datum, verify);
-			ret = -EIO;
-		}
-	}
-
-	DISABLE_VPP(map);
-	chip->state = FL_READY;
-	wake_up(&chip->wq);
-	spin_unlock_bh(chip->mutex);
-
-	return ret;
-}
-
-
-
-static int amd_flash_write(struct mtd_info *mtd, loff_t to , size_t len,
-			   size_t *retlen, const u_char *buf)
-{
-	struct map_info *map = mtd->priv;
-	struct amd_flash_private *private = map->fldrv_priv;
-	int ret = 0;
-	int chipnum;
-	unsigned long ofs;
-	unsigned long chipstart;
-
-	*retlen = 0;
-	if (!len) {
-		return 0;
-	}
-
-	chipnum = to >> private->chipshift;
-	ofs = to  - (chipnum << private->chipshift);
-	chipstart = private->chips[chipnum].start;
-
-	/* If it's not bus-aligned, do the first byte write. */
-	if (ofs & (map->buswidth - 1)) {
-		unsigned long bus_ofs = ofs & ~(map->buswidth - 1);
-		int i = ofs - bus_ofs;
-		int n = 0;
-		u_char tmp_buf[4];
-		__u32 datum;
-
-		map_copy_from(map, tmp_buf,
-			       bus_ofs + private->chips[chipnum].start,
-			       map->buswidth);
-		while (len && i < map->buswidth)
-			tmp_buf[i++] = buf[n++], len--;
-
-		if (map->buswidth == 2) {
-			datum = *(__u16*)tmp_buf;
-		} else if (map->buswidth == 4) {
-			datum = *(__u32*)tmp_buf;
-		} else {
-			return -EINVAL;  /* should never happen, but be safe */
-		}
-
-		ret = write_one_word(map, &private->chips[chipnum], bus_ofs,
-				     datum);
-		if (ret) {
-			return ret;
-		}
-
-		ofs += n;
-		buf += n;
-		(*retlen) += n;
-
-		if (ofs >> private->chipshift) {
-			chipnum++;
-			ofs = 0;
-			if (chipnum == private->numchips) {
-				return 0;
-			}
-		}
-	}
-
-	/* We are now aligned, write as much as possible. */
-	while(len >= map->buswidth) {
-		__u32 datum;
-
-		if (map->buswidth == 1) {
-			datum = *(__u8*)buf;
-		} else if (map->buswidth == 2) {
-			datum = *(__u16*)buf;
-		} else if (map->buswidth == 4) {
-			datum = *(__u32*)buf;
-		} else {
-			return -EINVAL;
-		}
-
-		ret = write_one_word(map, &private->chips[chipnum], ofs, datum);
-
-		if (ret) {
-			return ret;
-		}
-
-		ofs += map->buswidth;
-		buf += map->buswidth;
-		(*retlen) += map->buswidth;
-		len -= map->buswidth;
-
-		if (ofs >> private->chipshift) {
-			chipnum++;
-			ofs = 0;
-			if (chipnum == private->numchips) {
-				return 0;
-			}
-			chipstart = private->chips[chipnum].start;
-		}
-	}
-
-	if (len & (map->buswidth - 1)) {
-		int i = 0, n = 0;
-		u_char tmp_buf[2];
-		__u32 datum;
-
-		map_copy_from(map, tmp_buf,
-			       ofs + private->chips[chipnum].start,
-			       map->buswidth);
-		while (len--) {
-			tmp_buf[i++] = buf[n++];
-		}
-
-		if (map->buswidth == 2) {
-			datum = *(__u16*)tmp_buf;
-		} else if (map->buswidth == 4) {
-			datum = *(__u32*)tmp_buf;
-		} else {
-			return -EINVAL;  /* should never happen, but be safe */
-		}
-
-		ret = write_one_word(map, &private->chips[chipnum], ofs, datum);
-
-		if (ret) {
-			return ret;
-		}
-
-		(*retlen) += n;
-	}
-
-	return 0;
-}
-
-
-
-static inline int erase_one_block(struct map_info *map, struct flchip *chip,
-				  unsigned long adr, u_long size)
-{
-	unsigned long timeo = jiffies + HZ;
-	struct amd_flash_private *private = map->fldrv_priv;
-	DECLARE_WAITQUEUE(wait, current);
-
-retry:
-	spin_lock_bh(chip->mutex);
-
-	if (chip->state != FL_READY){
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-
-		spin_unlock_bh(chip->mutex);
-
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-
-		if (signal_pending(current)) {
-			return -EINTR;
-		}
-
-		timeo = jiffies + HZ;
-
-		goto retry;
-	}
-
-	chip->state = FL_ERASING;
-
-	adr += chip->start;
-	ENABLE_VPP(map);
-	send_cmd(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA);
-	send_cmd_to_addr(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA_2, adr);
-
-	timeo = jiffies + (HZ * 20);
-
-	spin_unlock_bh(chip->mutex);
-	msleep(1000);
-	spin_lock_bh(chip->mutex);
-
-	while (flash_is_busy(map, adr, private->interleave)) {
-
-		if (chip->state != FL_ERASING) {
-			/* Someone's suspended the erase. Sleep */
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			add_wait_queue(&chip->wq, &wait);
-
-			spin_unlock_bh(chip->mutex);
-			printk(KERN_INFO "%s: erase suspended. Sleeping\n",
-			       map->name);
-			schedule();
-			remove_wait_queue(&chip->wq, &wait);
-
-			if (signal_pending(current)) {
-				return -EINTR;
-			}
-
-			timeo = jiffies + (HZ*2); /* FIXME */
-			spin_lock_bh(chip->mutex);
-			continue;
-		}
-
-		/* OK Still waiting */
-		if (time_after(jiffies, timeo)) {
-			chip->state = FL_READY;
-			spin_unlock_bh(chip->mutex);
-			printk(KERN_WARNING "%s: waiting for erase to complete "
-			       "timed out.\n", map->name);
-			DISABLE_VPP(map);
-
-			return -EIO;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock_bh(chip->mutex);
-
-		if (need_resched())
-			schedule();
-		else
-			udelay(1);
-
-		spin_lock_bh(chip->mutex);
-	}
-
-	/* Verify every single word */
-	{
-		int address;
-		int error = 0;
-		__u8 verify;
-
-		for (address = adr; address < (adr + size); address++) {
-			if ((verify = map_read8(map, address)) != 0xFF) {
-				error = 1;
-				break;
-			}
-		}
-		if (error) {
-			chip->state = FL_READY;
-			spin_unlock_bh(chip->mutex);
-			printk(KERN_WARNING
-			       "%s: verify error at 0x%x, size %ld.\n",
-			       map->name, address, size);
-			DISABLE_VPP(map);
-
-			return -EIO;
-		}
-	}
-
-	DISABLE_VPP(map);
-	chip->state = FL_READY;
-	wake_up(&chip->wq);
-	spin_unlock_bh(chip->mutex);
-
-	return 0;
-}
-
-
-
-static int amd_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
-	struct map_info *map = mtd->priv;
-	struct amd_flash_private *private = map->fldrv_priv;
-	unsigned long adr, len;
-	int chipnum;
-	int ret = 0;
-	int i;
-	int first;
-	struct mtd_erase_region_info *regions = mtd->eraseregions;
-
-	if (instr->addr > mtd->size) {
-		return -EINVAL;
-	}
-
-	if ((instr->len + instr->addr) > mtd->size) {
-		return -EINVAL;
-	}
-
-	/* Check that both start and end of the requested erase are
-	 * aligned with the erasesize at the appropriate addresses.
-	 */
-
-	i = 0;
-
-        /* Skip all erase regions which are ended before the start of
-           the requested erase. Actually, to save on the calculations,
-           we skip to the first erase region which starts after the
-           start of the requested erase, and then go back one.
-        */
-
-        while ((i < mtd->numeraseregions) &&
-	       (instr->addr >= regions[i].offset)) {
-               i++;
-	}
-        i--;
-
-	/* OK, now i is pointing at the erase region in which this
-	 * erase request starts. Check the start of the requested
-	 * erase range is aligned with the erase size which is in
-	 * effect here.
-	 */
-
-	if (instr->addr & (regions[i].erasesize-1)) {
-		return -EINVAL;
-	}
-
-	/* Remember the erase region we start on. */
-
-	first = i;
-
-	/* Next, check that the end of the requested erase is aligned
-	 * with the erase region at that address.
-	 */
-
-	while ((i < mtd->numeraseregions) &&
-	       ((instr->addr + instr->len) >= regions[i].offset)) {
-                i++;
-	}
-
-	/* As before, drop back one to point at the region in which
-	 * the address actually falls.
-	 */
-
-	i--;
-
-	if ((instr->addr + instr->len) & (regions[i].erasesize-1)) {
-                return -EINVAL;
-	}
-
-	chipnum = instr->addr >> private->chipshift;
-	adr = instr->addr - (chipnum << private->chipshift);
-	len = instr->len;
-
-	i = first;
-
-	while (len) {
-		ret = erase_one_block(map, &private->chips[chipnum], adr,
-				      regions[i].erasesize);
-
-		if (ret) {
-			return ret;
-		}
-
-		adr += regions[i].erasesize;
-		len -= regions[i].erasesize;
-
-		if ((adr % (1 << private->chipshift)) ==
-		    ((regions[i].offset + (regions[i].erasesize *
-		    			   regions[i].numblocks))
-		     % (1 << private->chipshift))) {
-			i++;
-		}
-
-		if (adr >> private->chipshift) {
-			adr = 0;
-			chipnum++;
-			if (chipnum >= private->numchips) {
-				break;
-			}
-		}
-	}
-
-	instr->state = MTD_ERASE_DONE;
-	mtd_erase_callback(instr);
-
-	return 0;
-}
-
-
-
-static void amd_flash_sync(struct mtd_info *mtd)
-{
-	struct map_info *map = mtd->priv;
-	struct amd_flash_private *private = map->fldrv_priv;
-	int i;
-	struct flchip *chip;
-	int ret = 0;
-	DECLARE_WAITQUEUE(wait, current);
-
-	for (i = 0; !ret && (i < private->numchips); i++) {
-		chip = &private->chips[i];
-
-	retry:
-		spin_lock_bh(chip->mutex);
-
-		switch(chip->state) {
-		case FL_READY:
-		case FL_STATUS:
-		case FL_CFI_QUERY:
-		case FL_JEDEC_QUERY:
-			chip->oldstate = chip->state;
-			chip->state = FL_SYNCING;
-			/* No need to wake_up() on this state change -
-			 * as the whole point is that nobody can do anything
-			 * with the chip now anyway.
-			 */
-		case FL_SYNCING:
-			spin_unlock_bh(chip->mutex);
-			break;
-
-		default:
-			/* Not an idle state */
-			add_wait_queue(&chip->wq, &wait);
-
-			spin_unlock_bh(chip->mutex);
-
-			schedule();
-
-		        remove_wait_queue(&chip->wq, &wait);
-
-			goto retry;
-		}
-	}
-
-	/* Unlock the chips again */
-	for (i--; i >= 0; i--) {
-		chip = &private->chips[i];
-
-		spin_lock_bh(chip->mutex);
-
-		if (chip->state == FL_SYNCING) {
-			chip->state = chip->oldstate;
-			wake_up(&chip->wq);
-		}
-		spin_unlock_bh(chip->mutex);
-	}
-}
-
-
-
-static int amd_flash_suspend(struct mtd_info *mtd)
-{
-printk("amd_flash_suspend(): not implemented!\n");
-	return -EINVAL;
-}
-
-
-
-static void amd_flash_resume(struct mtd_info *mtd)
-{
-printk("amd_flash_resume(): not implemented!\n");
-}
-
-
-
-static void amd_flash_destroy(struct mtd_info *mtd)
-{
-	struct map_info *map = mtd->priv;
-	struct amd_flash_private *private = map->fldrv_priv;
-	kfree(private);
-}
-
-int __init amd_flash_init(void)
-{
-	register_mtd_chip_driver(&amd_flash_chipdrv);
-	return 0;
-}
-
-void __exit amd_flash_exit(void)
-{
-	unregister_mtd_chip_driver(&amd_flash_chipdrv);
-}
-
-module_init(amd_flash_init);
-module_exit(amd_flash_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jonas Holmberg <jonas.holmberg at axis.com>");
-MODULE_DESCRIPTION("Old MTD chip driver for AMD flash chips");
diff --git a/drivers/mtd/chips/jedec.c b/drivers/mtd/chips/jedec.c
deleted file mode 100644
index 14e57b2..0000000
--- a/drivers/mtd/chips/jedec.c
+++ /dev/null
@@ -1,935 +0,0 @@
-
-/* JEDEC Flash Interface.
- * This is an older type of interface for self programming flash. It is
- * commonly use in older AMD chips and is obsolete compared with CFI.
- * It is called JEDEC because the JEDEC association distributes the ID codes
- * for the chips.
- *
- * See the AMD flash databook for information on how to operate the interface.
- *
- * This code does not support anything wider than 8 bit flash chips, I am
- * not going to guess how to send commands to them, plus I expect they will
- * all speak CFI..
- *
- * $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/jedec.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/compatmac.h>
-
-static struct mtd_info *jedec_probe(struct map_info *);
-static int jedec_probe8(struct map_info *map,unsigned long base,
-		  struct jedec_private *priv);
-static int jedec_probe16(struct map_info *map,unsigned long base,
-		  struct jedec_private *priv);
-static int jedec_probe32(struct map_info *map,unsigned long base,
-		  struct jedec_private *priv);
-static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start,
-			    unsigned long len);
-static int flash_erase(struct mtd_info *mtd, struct erase_info *instr);
-static int flash_write(struct mtd_info *mtd, loff_t start, size_t len,
-		       size_t *retlen, const u_char *buf);
-
-static unsigned long my_bank_size;
-
-/* Listing of parts and sizes. We need this table to learn the sector
-   size of the chip and the total length */
-static const struct JEDECTable JEDEC_table[] = {
-	{
-		.jedec		= 0x013D,
-		.name		= "AMD Am29F017D",
-		.size		= 2*1024*1024,
-		.sectorsize	= 64*1024,
-		.capabilities	= MTD_CAP_NORFLASH
-	},
-	{
-		.jedec		= 0x01AD,
-		.name		= "AMD Am29F016",
-		.size		= 2*1024*1024,
-		.sectorsize	= 64*1024,
-		.capabilities	= MTD_CAP_NORFLASH
-	},
-	{
-		.jedec		= 0x01D5,
-		.name		= "AMD Am29F080",
-		.size		= 1*1024*1024,
-		.sectorsize	= 64*1024,
-		.capabilities	= MTD_CAP_NORFLASH
-	},
-	{
-		.jedec		= 0x01A4,
-		.name		= "AMD Am29F040",
-		.size		= 512*1024,
-		.sectorsize	= 64*1024,
-		.capabilities	= MTD_CAP_NORFLASH
-	},
-	{
-		.jedec		= 0x20E3,
-		.name		= "AMD Am29W040B",
-		.size		= 512*1024,
-		.sectorsize	= 64*1024,
-		.capabilities	= MTD_CAP_NORFLASH
-	},
-	{
-		.jedec		= 0xC2AD,
-		.name		= "Macronix MX29F016",
-		.size		= 2*1024*1024,
-		.sectorsize	= 64*1024,
-		.capabilities	= MTD_CAP_NORFLASH
-	},
-	{ .jedec = 0x0 }
-};
-
-static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id);
-static void jedec_sync(struct mtd_info *mtd) {};
-static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len,
-		      size_t *retlen, u_char *buf);
-static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len,
-			     size_t *retlen, u_char *buf);
-
-static struct mtd_info *jedec_probe(struct map_info *map);
-
-
-
-static struct mtd_chip_driver jedec_chipdrv = {
-	.probe	= jedec_probe,
-	.name	= "jedec",
-	.module	= THIS_MODULE
-};
-
-/* Probe entry point */
-
-static struct mtd_info *jedec_probe(struct map_info *map)
-{
-   struct mtd_info *MTD;
-   struct jedec_private *priv;
-   unsigned long Base;
-   unsigned long SectorSize;
-   unsigned count;
-   unsigned I,Uniq;
-   char Part[200];
-   memset(&priv,0,sizeof(priv));
-
-   MTD = kzalloc(sizeof(struct mtd_info) + sizeof(struct jedec_private), GFP_KERNEL);
-   if (!MTD)
-	   return NULL;
-
-   priv = (struct jedec_private *)&MTD[1];
-
-   my_bank_size = map->size;
-
-   if (map->size/my_bank_size > MAX_JEDEC_CHIPS)
-   {
-      printk("mtd: Increase MAX_JEDEC_CHIPS, too many banks.\n");
-      kfree(MTD);
-      return NULL;
-   }
-
-   for (Base = 0; Base < map->size; Base += my_bank_size)
-   {
-      // Perhaps zero could designate all tests?
-      if (map->buswidth == 0)
-	 map->buswidth = 1;
-
-      if (map->buswidth == 1){
-	 if (jedec_probe8(map,Base,priv) == 0) {
-		 printk("did recognize jedec chip\n");
-		 kfree(MTD);
-	         return NULL;
-	 }
-      }
-      if (map->buswidth == 2)
-	 jedec_probe16(map,Base,priv);
-      if (map->buswidth == 4)
-	 jedec_probe32(map,Base,priv);
-   }
-
-   // Get the biggest sector size
-   SectorSize = 0;
-   for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
-   {
-	   //	   printk("priv->chips[%d].jedec is %x\n",I,priv->chips[I].jedec);
-	   //	   printk("priv->chips[%d].sectorsize is %lx\n",I,priv->chips[I].sectorsize);
-      if (priv->chips[I].sectorsize > SectorSize)
-	 SectorSize = priv->chips[I].sectorsize;
-   }
-
-   // Quickly ensure that the other sector sizes are factors of the largest
-   for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
-   {
-      if ((SectorSize/priv->chips[I].sectorsize)*priv->chips[I].sectorsize != SectorSize)
-      {
-	 printk("mtd: Failed. Device has incompatible mixed sector sizes\n");
-	 kfree(MTD);
-	 return NULL;
-      }
-   }
-
-   /* Generate a part name that includes the number of different chips and
-      other configuration information */
-   count = 1;
-   strlcpy(Part,map->name,sizeof(Part)-10);
-   strcat(Part," ");
-   Uniq = 0;
-   for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
-   {
-      const struct JEDECTable *JEDEC;
-
-      if (priv->chips[I+1].jedec == priv->chips[I].jedec)
-      {
-	 count++;
-	 continue;
-      }
-
-      // Locate the chip in the jedec table
-      JEDEC = jedec_idtoinf(priv->chips[I].jedec >> 8,priv->chips[I].jedec);
-      if (JEDEC == 0)
-      {
-	 printk("mtd: Internal Error, JEDEC not set\n");
-	 kfree(MTD);
-	 return NULL;
-      }
-
-      if (Uniq != 0)
-	 strcat(Part,",");
-      Uniq++;
-
-      if (count != 1)
-	 sprintf(Part+strlen(Part),"%x*[%s]",count,JEDEC->name);
-      else
-	 sprintf(Part+strlen(Part),"%s",JEDEC->name);
-      if (strlen(Part) > sizeof(Part)*2/3)
-	 break;
-      count = 1;
-   }
-
-   /* Determine if the chips are organized in a linear fashion, or if there
-      are empty banks. Note, the last bank does not count here, only the
-      first banks are important. Holes on non-bank boundaries can not exist
-      due to the way the detection algorithm works. */
-   if (priv->size < my_bank_size)
-      my_bank_size = priv->size;
-   priv->is_banked = 0;
-   //printk("priv->size is %x, my_bank_size is %x\n",priv->size,my_bank_size);
-   //printk("priv->bank_fill[0] is %x\n",priv->bank_fill[0]);
-   if (!priv->size) {
-	   printk("priv->size is zero\n");
-	   kfree(MTD);
-	   return NULL;
-   }
-   if (priv->size/my_bank_size) {
-	   if (priv->size/my_bank_size == 1) {
-		   priv->size = my_bank_size;
-	   }
-	   else {
-		   for (I = 0; I != priv->size/my_bank_size - 1; I++)
-		   {
-		      if (priv->bank_fill[I] != my_bank_size)
-			 priv->is_banked = 1;
-
-		      /* This even could be eliminated, but new de-optimized read/write
-			 functions have to be written */
-		      printk("priv->bank_fill[%d] is %lx, priv->bank_fill[0] is %lx\n",I,priv->bank_fill[I],priv->bank_fill[0]);
-		      if (priv->bank_fill[I] != priv->bank_fill[0])
-		      {
-			 printk("mtd: Failed. Cannot handle unsymmetric banking\n");
-			 kfree(MTD);
-			 return NULL;
-		      }
-		   }
-	   }
-   }
-   if (priv->is_banked == 1)
-      strcat(Part,", banked");
-
-   //   printk("Part: '%s'\n",Part);
-
-   memset(MTD,0,sizeof(*MTD));
-  // strlcpy(MTD->name,Part,sizeof(MTD->name));
-   MTD->name = map->name;
-   MTD->type = MTD_NORFLASH;
-   MTD->flags = MTD_CAP_NORFLASH;
-   MTD->writesize = 1;
-   MTD->erasesize = SectorSize*(map->buswidth);
-   //   printk("MTD->erasesize is %x\n",(unsigned int)MTD->erasesize);
-   MTD->size = priv->size;
-   //   printk("MTD->size is %x\n",(unsigned int)MTD->size);
-   //MTD->module = THIS_MODULE; // ? Maybe this should be the low level module?
-   MTD->erase = flash_erase;
-   if (priv->is_banked == 1)
-      MTD->read = jedec_read_banked;
-   else
-      MTD->read = jedec_read;
-   MTD->write = flash_write;
-   MTD->sync = jedec_sync;
-   MTD->priv = map;
-   map->fldrv_priv = priv;
-   map->fldrv = &jedec_chipdrv;
-   __module_get(THIS_MODULE);
-   return MTD;
-}
-
-/* Helper for the JEDEC function, JEDEC numbers all have odd parity */
-static int checkparity(u_char C)
-{
-   u_char parity = 0;
-   while (C != 0)
-   {
-      parity ^= C & 1;
-      C >>= 1;
-   }
-
-   return parity == 1;
-}
-
-
-/* Take an array of JEDEC numbers that represent interleved flash chips
-   and process them. Check to make sure they are good JEDEC numbers, look
-   them up and then add them to the chip list */
-static int handle_jedecs(struct map_info *map,__u8 *Mfg,__u8 *Id,unsigned Count,
-		  unsigned long base,struct jedec_private *priv)
-{
-   unsigned I,J;
-   unsigned long Size;
-   unsigned long SectorSize;
-   const struct JEDECTable *JEDEC;
-
-   // Test #2 JEDEC numbers exhibit odd parity
-   for (I = 0; I != Count; I++)
-   {
-      if (checkparity(Mfg[I]) == 0 || checkparity(Id[I]) == 0)
-	 return 0;
-   }
-
-   // Finally, just make sure all the chip sizes are the same
-   JEDEC = jedec_idtoinf(Mfg[0],Id[0]);
-
-   if (JEDEC == 0)
-   {
-      printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]);
-      return 0;
-   }
-
-   Size = JEDEC->size;
-   SectorSize = JEDEC->sectorsize;
-   for (I = 0; I != Count; I++)
-   {
-      JEDEC = jedec_idtoinf(Mfg[0],Id[0]);
-      if (JEDEC == 0)
-      {
-	 printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]);
-	 return 0;
-      }
-
-      if (Size != JEDEC->size || SectorSize != JEDEC->sectorsize)
-      {
-	 printk("mtd: Failed. Interleved flash does not have matching characteristics\n");
-	 return 0;
-      }
-   }
-
-   // Load the Chips
-   for (I = 0; I != MAX_JEDEC_CHIPS; I++)
-   {
-      if (priv->chips[I].jedec == 0)
-	 break;
-   }
-
-   if (I + Count > MAX_JEDEC_CHIPS)
-   {
-      printk("mtd: Device has too many chips. Increase MAX_JEDEC_CHIPS\n");
-      return 0;
-   }
-
-   // Add them to the table
-   for (J = 0; J != Count; J++)
-   {
-      unsigned long Bank;
-
-      JEDEC = jedec_idtoinf(Mfg[J],Id[J]);
-      priv->chips[I].jedec = (Mfg[J] << 8) | Id[J];
-      priv->chips[I].size = JEDEC->size;
-      priv->chips[I].sectorsize = JEDEC->sectorsize;
-      priv->chips[I].base = base + J;
-      priv->chips[I].datashift = J*8;
-      priv->chips[I].capabilities = JEDEC->capabilities;
-      priv->chips[I].offset = priv->size + J;
-
-      // log2 n :|
-      priv->chips[I].addrshift = 0;
-      for (Bank = Count; Bank != 1; Bank >>= 1, priv->chips[I].addrshift++);
-
-      // Determine how filled this bank is.
-      Bank = base & (~(my_bank_size-1));
-      if (priv->bank_fill[Bank/my_bank_size] < base +
-	  (JEDEC->size << priv->chips[I].addrshift) - Bank)
-	 priv->bank_fill[Bank/my_bank_size] =  base + (JEDEC->size << priv->chips[I].addrshift) - Bank;
-      I++;
-   }
-
-   priv->size += priv->chips[I-1].size*Count;
-
-   return priv->chips[I-1].size;
-}
-
-/* Lookup the chip information from the JEDEC ID table. */
-static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id)
-{
-   __u16 Id = (mfr << 8) | id;
-   unsigned long I = 0;
-   for (I = 0; JEDEC_table[I].jedec != 0; I++)
-      if (JEDEC_table[I].jedec == Id)
-	 return JEDEC_table + I;
-   return NULL;
-}
-
-// Look for flash using an 8 bit bus interface
-static int jedec_probe8(struct map_info *map,unsigned long base,
-		  struct jedec_private *priv)
-{
-   #define flread(x) map_read8(map,base+x)
-   #define flwrite(v,x) map_write8(map,v,base+x)
-
-   const unsigned long AutoSel1 = 0xAA;
-   const unsigned long AutoSel2 = 0x55;
-   const unsigned long AutoSel3 = 0x90;
-   const unsigned long Reset = 0xF0;
-   __u32 OldVal;
-   __u8 Mfg[1];
-   __u8 Id[1];
-   unsigned I;
-   unsigned long Size;
-
-   // Wait for any write/erase operation to settle
-   OldVal = flread(base);
-   for (I = 0; OldVal != flread(base) && I < 10000; I++)
-      OldVal = flread(base);
-
-   // Reset the chip
-   flwrite(Reset,0x555);
-
-   // Send the sequence
-   flwrite(AutoSel1,0x555);
-   flwrite(AutoSel2,0x2AA);
-   flwrite(AutoSel3,0x555);
-
-   //  Get the JEDEC numbers
-   Mfg[0] = flread(0);
-   Id[0] = flread(1);
-   //   printk("Mfg is %x, Id is %x\n",Mfg[0],Id[0]);
-
-   Size = handle_jedecs(map,Mfg,Id,1,base,priv);
-   //   printk("handle_jedecs Size is %x\n",(unsigned int)Size);
-   if (Size == 0)
-   {
-      flwrite(Reset,0x555);
-      return 0;
-   }
-
-
-   // Reset.
-   flwrite(Reset,0x555);
-
-   return 1;
-
-   #undef flread
-   #undef flwrite
-}
-
-// Look for flash using a 16 bit bus interface (ie 2 8-bit chips)
-static int jedec_probe16(struct map_info *map,unsigned long base,
-		  struct jedec_private *priv)
-{
-   return 0;
-}
-
-// Look for flash using a 32 bit bus interface (ie 4 8-bit chips)
-static int jedec_probe32(struct map_info *map,unsigned long base,
-		  struct jedec_private *priv)
-{
-   #define flread(x) map_read32(map,base+((x)<<2))
-   #define flwrite(v,x) map_write32(map,v,base+((x)<<2))
-
-   const unsigned long AutoSel1 = 0xAAAAAAAA;
-   const unsigned long AutoSel2 = 0x55555555;
-   const unsigned long AutoSel3 = 0x90909090;
-   const unsigned long Reset = 0xF0F0F0F0;
-   __u32 OldVal;
-   __u8 Mfg[4];
-   __u8 Id[4];
-   unsigned I;
-   unsigned long Size;
-
-   // Wait for any write/erase operation to settle
-   OldVal = flread(base);
-   for (I = 0; OldVal != flread(base) && I < 10000; I++)
-      OldVal = flread(base);
-
-   // Reset the chip
-   flwrite(Reset,0x555);
-
-   // Send the sequence
-   flwrite(AutoSel1,0x555);
-   flwrite(AutoSel2,0x2AA);
-   flwrite(AutoSel3,0x555);
-
-   // Test #1, JEDEC numbers are readable from 0x??00/0x??01
-   if (flread(0) != flread(0x100) ||
-       flread(1) != flread(0x101))
-   {
-      flwrite(Reset,0x555);
-      return 0;
-   }
-
-   // Split up the JEDEC numbers
-   OldVal = flread(0);
-   for (I = 0; I != 4; I++)
-      Mfg[I] = (OldVal >> (I*8));
-   OldVal = flread(1);
-   for (I = 0; I != 4; I++)
-      Id[I] = (OldVal >> (I*8));
-
-   Size = handle_jedecs(map,Mfg,Id,4,base,priv);
-   if (Size == 0)
-   {
-      flwrite(Reset,0x555);
-      return 0;
-   }
-
-   /* Check if there is address wrap around within a single bank, if this
-      returns JEDEC numbers then we assume that it is wrap around. Notice
-      we call this routine with the JEDEC return still enabled, if two or
-      more flashes have a truncated address space the probe test will still
-      work */
-   if (base + (Size<<2)+0x555 < map->size &&
-       base + (Size<<2)+0x555 < (base & (~(my_bank_size-1))) + my_bank_size)
-   {
-      if (flread(base+Size) != flread(base+Size + 0x100) ||
-	  flread(base+Size + 1) != flread(base+Size + 0x101))
-      {
-	 jedec_probe32(map,base+Size,priv);
-      }
-   }
-
-   // Reset.
-   flwrite(0xF0F0F0F0,0x555);
-
-   return 1;
-
-   #undef flread
-   #undef flwrite
-}
-
-/* Linear read. */
-static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len,
-		      size_t *retlen, u_char *buf)
-{
-   struct map_info *map = mtd->priv;
-
-   map_copy_from(map, buf, from, len);
-   *retlen = len;
-   return 0;
-}
-
-/* Banked read. Take special care to jump past the holes in the bank
-   mapping. This version assumes symetry in the holes.. */
-static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len,
-			     size_t *retlen, u_char *buf)
-{
-   struct map_info *map = mtd->priv;
-   struct jedec_private *priv = map->fldrv_priv;
-
-   *retlen = 0;
-   while (len > 0)
-   {
-      // Determine what bank and offset into that bank the first byte is
-      unsigned long bank = from & (~(priv->bank_fill[0]-1));
-      unsigned long offset = from & (priv->bank_fill[0]-1);
-      unsigned long get = len;
-      if (priv->bank_fill[0] - offset < len)
-	 get = priv->bank_fill[0] - offset;
-
-      bank /= priv->bank_fill[0];
-      map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
-
-      len -= get;
-      *retlen += get;
-      from += get;
-   }
-   return 0;
-}
-
-/* Pass the flags value that the flash return before it re-entered read
-   mode. */
-static void jedec_flash_failed(unsigned char code)
-{
-   /* Bit 5 being high indicates that there was an internal device
-      failure, erasure time limits exceeded or something */
-   if ((code & (1 << 5)) != 0)
-   {
-      printk("mtd: Internal Flash failure\n");
-      return;
-   }
-   printk("mtd: Programming didn't take\n");
-}
-
-/* This uses the erasure function described in the AMD Flash Handbook,
-   it will work for flashes with a fixed sector size only. Flashes with
-   a selection of sector sizes (ie the AMD Am29F800B) will need a different
-   routine. This routine tries to parallize erasing multiple chips/sectors
-   where possible */
-static int flash_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
-   // Does IO to the currently selected chip
-   #define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift))
-   #define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift))
-
-   unsigned long Time = 0;
-   unsigned long NoTime = 0;
-   unsigned long start = instr->addr, len = instr->len;
-   unsigned int I;
-   struct map_info *map = mtd->priv;
-   struct jedec_private *priv = map->fldrv_priv;
-
-   // Verify the arguments..
-   if (start + len > mtd->size ||
-       (start % mtd->erasesize) != 0 ||
-       (len % mtd->erasesize) != 0 ||
-       (len/mtd->erasesize) == 0)
-      return -EINVAL;
-
-   jedec_flash_chip_scan(priv,start,len);
-
-   // Start the erase sequence on each chip
-   for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
-   {
-      unsigned long off;
-      struct jedec_flash_chip *chip = priv->chips + I;
-
-      if (chip->length == 0)
-	 continue;
-
-      if (chip->start + chip->length > chip->size)
-      {
-	 printk("DIE\n");
-	 return -EIO;
-      }
-
-      flwrite(0xF0,chip->start + 0x555);
-      flwrite(0xAA,chip->start + 0x555);
-      flwrite(0x55,chip->start + 0x2AA);
-      flwrite(0x80,chip->start + 0x555);
-      flwrite(0xAA,chip->start + 0x555);
-      flwrite(0x55,chip->start + 0x2AA);
-
-      /* Once we start selecting the erase sectors the delay between each
-         command must not exceed 50us or it will immediately start erasing
-         and ignore the other sectors */
-      for (off = 0; off < len; off += chip->sectorsize)
-      {
-	 // Check to make sure we didn't timeout
-	 flwrite(0x30,chip->start + off);
-	 if (off == 0)
-	    continue;
-	 if ((flread(chip->start + off) & (1 << 3)) != 0)
-	 {
-	    printk("mtd: Ack! We timed out the erase timer!\n");
-	    return -EIO;
-	 }
-      }
-   }
-
-   /* We could split this into a timer routine and return early, performing
-      background erasure.. Maybe later if the need warrents */
-
-   /* Poll the flash for erasure completion, specs say this can take as long
-      as 480 seconds to do all the sectors (for a 2 meg flash).
-      Erasure time is dependent on chip age, temp and wear.. */
-
-   /* This being a generic routine assumes a 32 bit bus. It does read32s
-      and bundles interleved chips into the same grouping. This will work
-      for all bus widths */
-   Time = 0;
-   NoTime = 0;
-   for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
-   {
-      struct jedec_flash_chip *chip = priv->chips + I;
-      unsigned long off = 0;
-      unsigned todo[4] = {0,0,0,0};
-      unsigned todo_left = 0;
-      unsigned J;
-
-      if (chip->length == 0)
-	 continue;
-
-      /* Find all chips in this data line, realistically this is all
-         or nothing up to the interleve count */
-      for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++)
-      {
-	 if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) ==
-	     (chip->base & (~((1<<chip->addrshift)-1))))
-	 {
-	    todo_left++;
-	    todo[priv->chips[J].base & ((1<<chip->addrshift)-1)] = 1;
-	 }
-      }
-
-      /*      printk("todo: %x %x %x %x\n",(short)todo[0],(short)todo[1],
-	      (short)todo[2],(short)todo[3]);
-      */
-      while (1)
-      {
-	 __u32 Last[4];
-	 unsigned long Count = 0;
-
-	 /* During erase bit 7 is held low and bit 6 toggles, we watch this,
-	    should it stop toggling or go high then the erase is completed,
-  	    or this is not really flash ;> */
-	 switch (map->buswidth) {
-	 case 1:
-	    Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    break;
-	 case 2:
-	    Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    break;
-	 case 3:
-	    Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    break;
-	 }
-	 Count = 3;
-	 while (todo_left != 0)
-	 {
-	    for (J = 0; J != 4; J++)
-	    {
-	       __u8 Byte1 = (Last[(Count-1)%4] >> (J*8)) & 0xFF;
-	       __u8 Byte2 = (Last[(Count-2)%4] >> (J*8)) & 0xFF;
-	       __u8 Byte3 = (Last[(Count-3)%4] >> (J*8)) & 0xFF;
-	       if (todo[J] == 0)
-		  continue;
-
-	       if ((Byte1 & (1 << 7)) == 0 && Byte1 != Byte2)
-	       {
-//		  printk("Check %x %x %x\n",(short)J,(short)Byte1,(short)Byte2);
-		  continue;
-	       }
-
-	       if (Byte1 == Byte2)
-	       {
-		  jedec_flash_failed(Byte3);
-		  return -EIO;
-	       }
-
-	       todo[J] = 0;
-	       todo_left--;
-	    }
-
-/*	    if (NoTime == 0)
-	       Time += HZ/10 - schedule_timeout(HZ/10);*/
-	    NoTime = 0;
-
-	    switch (map->buswidth) {
-	    case 1:
-	       Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
-	      break;
-	    case 2:
-	       Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
-	      break;
-	    case 4:
-	       Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
-	      break;
-	    }
-	    Count++;
-
-/*	    // Count time, max of 15s per sector (according to AMD)
-	    if (Time > 15*len/mtd->erasesize*HZ)
-	    {
-	       printk("mtd: Flash Erase Timed out\n");
-	       return -EIO;
-	    }	    */
-	 }
-
-	 // Skip to the next chip if we used chip erase
-	 if (chip->length == chip->size)
-	    off = chip->size;
-	 else
-	    off += chip->sectorsize;
-
-	 if (off >= chip->length)
-	    break;
-	 NoTime = 1;
-      }
-
-      for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++)
-      {
-	 if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) ==
-	     (chip->base & (~((1<<chip->addrshift)-1))))
-	    priv->chips[J].length = 0;
-      }
-   }
-
-   //printk("done\n");
-   instr->state = MTD_ERASE_DONE;
-   mtd_erase_callback(instr);
-   return 0;
-
-   #undef flread
-   #undef flwrite
-}
-
-/* This is the simple flash writing function. It writes to every byte, in
-   sequence. It takes care of how to properly address the flash if
-   the flash is interleved. It can only be used if all the chips in the
-   array are identical!*/
-static int flash_write(struct mtd_info *mtd, loff_t start, size_t len,
-		       size_t *retlen, const u_char *buf)
-{
-   /* Does IO to the currently selected chip. It takes the bank addressing
-      base (which is divisible by the chip size) adds the necessary lower bits
-      of addrshift (interleave index) and then adds the control register index. */
-   #define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
-   #define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
-
-   struct map_info *map = mtd->priv;
-   struct jedec_private *priv = map->fldrv_priv;
-   unsigned long base;
-   unsigned long off;
-   size_t save_len = len;
-
-   if (start + len > mtd->size)
-      return -EIO;
-
-   //printk("Here");
-
-   //printk("flash_write: start is %x, len is %x\n",start,(unsigned long)len);
-   while (len != 0)
-   {
-      struct jedec_flash_chip *chip = priv->chips;
-      unsigned long bank;
-      unsigned long boffset;
-
-      // Compute the base of the flash.
-      off = ((unsigned long)start) % (chip->size << chip->addrshift);
-      base = start - off;
-
-      // Perform banked addressing translation.
-      bank = base & (~(priv->bank_fill[0]-1));
-      boffset = base & (priv->bank_fill[0]-1);
-      bank = (bank/priv->bank_fill[0])*my_bank_size;
-      base = bank + boffset;
-
-    //  printk("Flasing %X %X %X\n",base,chip->size,len);
-     // printk("off is %x, compare with %x\n",off,chip->size << chip->addrshift);
-
-      // Loop over this page
-      for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++)
-      {
-	 unsigned char oldbyte = map_read8(map,base+off);
-	 unsigned char Last[4];
-	 unsigned long Count = 0;
-
-	 if (oldbyte == *buf) {
-	//	 printk("oldbyte and *buf is %x,len is %x\n",oldbyte,len);
-	    continue;
-	 }
-	 if (((~oldbyte) & *buf) != 0)
-	    printk("mtd: warn: Trying to set a 0 to a 1\n");
-
-	 // Write
-	 flwrite(0xAA,0x555);
-	 flwrite(0x55,0x2AA);
-	 flwrite(0xA0,0x555);
-	 map_write8(map,*buf,base + off);
-	 Last[0] = map_read8(map,base + off);
-	 Last[1] = map_read8(map,base + off);
-	 Last[2] = map_read8(map,base + off);
-
-	 /* Wait for the flash to finish the operation. We store the last 4
-	    status bytes that have been retrieved so we can determine why
-	    it failed. The toggle bits keep toggling when there is a
-	    failure */
-	 for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] &&
-	      Count < 10000; Count++)
-	    Last[Count % 4] = map_read8(map,base + off);
-	 if (Last[(Count - 1) % 4] != *buf)
-	 {
-	    jedec_flash_failed(Last[(Count - 3) % 4]);
-	    return -EIO;
-	 }
-      }
-   }
-   *retlen = save_len;
-   return 0;
-}
-
-/* This is used to enhance the speed of the erase routine,
-   when things are being done to multiple chips it is possible to
-   parallize the operations, particularly full memory erases of multi
-   chip memories benifit */
-static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start,
-		     unsigned long len)
-{
-   unsigned int I;
-
-   // Zero the records
-   for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
-      priv->chips[I].start = priv->chips[I].length = 0;
-
-   // Intersect the region with each chip
-   for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
-   {
-      struct jedec_flash_chip *chip = priv->chips + I;
-      unsigned long ByteStart;
-      unsigned long ChipEndByte = chip->offset + (chip->size << chip->addrshift);
-
-      // End is before this chip or the start is after it
-      if (start+len < chip->offset ||
-	  ChipEndByte - (1 << chip->addrshift) < start)
-	 continue;
-
-      if (start < chip->offset)
-      {
-	 ByteStart = chip->offset;
-	 chip->start = 0;
-      }
-      else
-      {
-	 chip->start = (start - chip->offset + (1 << chip->addrshift)-1) >> chip->addrshift;
-	 ByteStart = start;
-      }
-
-      if (start + len >= ChipEndByte)
-	 chip->length = (ChipEndByte - ByteStart) >> chip->addrshift;
-      else
-	 chip->length = (start + len - ByteStart + (1 << chip->addrshift)-1) >> chip->addrshift;
-   }
-}
-
-int __init jedec_init(void)
-{
-	register_mtd_chip_driver(&jedec_chipdrv);
-	return 0;
-}
-
-static void __exit jedec_exit(void)
-{
-	unregister_mtd_chip_driver(&jedec_chipdrv);
-}
-
-module_init(jedec_init);
-module_exit(jedec_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jason Gunthorpe <jgg at deltatee.com> et al.");
-MODULE_DESCRIPTION("Old MTD chip driver for JEDEC-compliant flash chips");
diff --git a/drivers/mtd/chips/sharp.c b/drivers/mtd/chips/sharp.c
deleted file mode 100644
index c9cd3d2..0000000
--- a/drivers/mtd/chips/sharp.c
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * MTD chip driver for pre-CFI Sharp flash chips
- *
- * Copyright 2000,2001 David A. Schleef <ds at schleef.org>
- *           2000,2001 Lineo, Inc.
- *
- * $Id: sharp.c,v 1.17 2005/11/29 14:28:28 gleixner Exp $
- *
- * Devices supported:
- *   LH28F016SCT Symmetrical block flash memory, 2Mx8
- *   LH28F008SCT Symmetrical block flash memory, 1Mx8
- *
- * Documentation:
- *   http://www.sharpmeg.com/datasheets/memic/flashcmp/
- *   http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf
- *   016sctl9.pdf
- *
- * Limitations:
- *   This driver only supports 4x1 arrangement of chips.
- *   Not tested on anything but PowerPC.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/cfi.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-
-#define CMD_RESET		0xffffffff
-#define CMD_READ_ID		0x90909090
-#define CMD_READ_STATUS		0x70707070
-#define CMD_CLEAR_STATUS	0x50505050
-#define CMD_BLOCK_ERASE_1	0x20202020
-#define CMD_BLOCK_ERASE_2	0xd0d0d0d0
-#define CMD_BYTE_WRITE		0x40404040
-#define CMD_SUSPEND		0xb0b0b0b0
-#define CMD_RESUME		0xd0d0d0d0
-#define CMD_SET_BLOCK_LOCK_1	0x60606060
-#define CMD_SET_BLOCK_LOCK_2	0x01010101
-#define CMD_SET_MASTER_LOCK_1	0x60606060
-#define CMD_SET_MASTER_LOCK_2	0xf1f1f1f1
-#define CMD_CLEAR_BLOCK_LOCKS_1	0x60606060
-#define CMD_CLEAR_BLOCK_LOCKS_2	0xd0d0d0d0
-
-#define SR_READY		0x80808080 // 1 = ready
-#define SR_ERASE_SUSPEND	0x40404040 // 1 = block erase suspended
-#define SR_ERROR_ERASE		0x20202020 // 1 = error in block erase or clear lock bits
-#define SR_ERROR_WRITE		0x10101010 // 1 = error in byte write or set lock bit
-#define	SR_VPP			0x08080808 // 1 = Vpp is low
-#define SR_WRITE_SUSPEND	0x04040404 // 1 = byte write suspended
-#define SR_PROTECT		0x02020202 // 1 = lock bit set
-#define SR_RESERVED		0x01010101
-
-#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT)
-
-/* Configuration options */
-
-#undef AUTOUNLOCK  /* automatically unlocks blocks before erasing */
-
-static struct mtd_info *sharp_probe(struct map_info *);
-
-static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd);
-
-static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
-	size_t *retlen, u_char *buf);
-static int sharp_write(struct mtd_info *mtd, loff_t from, size_t len,
-	size_t *retlen, const u_char *buf);
-static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr);
-static void sharp_sync(struct mtd_info *mtd);
-static int sharp_suspend(struct mtd_info *mtd);
-static void sharp_resume(struct mtd_info *mtd);
-static void sharp_destroy(struct mtd_info *mtd);
-
-static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
-	unsigned long adr, __u32 datum);
-static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
-	unsigned long adr);
-#ifdef AUTOUNLOCK
-static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
-	unsigned long adr);
-#endif
-
-
-struct sharp_info{
-	struct flchip *chip;
-	int bogus;
-	int chipshift;
-	int numchips;
-	struct flchip chips[1];
-};
-
-static void sharp_destroy(struct mtd_info *mtd);
-
-static struct mtd_chip_driver sharp_chipdrv = {
-	.probe		= sharp_probe,
-	.destroy	= sharp_destroy,
-	.name		= "sharp",
-	.module		= THIS_MODULE
-};
-
-
-static struct mtd_info *sharp_probe(struct map_info *map)
-{
-	struct mtd_info *mtd = NULL;
-	struct sharp_info *sharp = NULL;
-	int width;
-
-	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
-	if(!mtd)
-		return NULL;
-
-	sharp = kzalloc(sizeof(*sharp), GFP_KERNEL);
-	if(!sharp) {
-		kfree(mtd);
-		return NULL;
-	}
-
-	width = sharp_probe_map(map,mtd);
-	if(!width){
-		kfree(mtd);
-		kfree(sharp);
-		return NULL;
-	}
-
-	mtd->priv = map;
-	mtd->type = MTD_NORFLASH;
-	mtd->erase = sharp_erase;
-	mtd->read = sharp_read;
-	mtd->write = sharp_write;
-	mtd->sync = sharp_sync;
-	mtd->suspend = sharp_suspend;
-	mtd->resume = sharp_resume;
-	mtd->flags = MTD_CAP_NORFLASH;
-	mtd->writesize = 1;
-	mtd->name = map->name;
-
-	sharp->chipshift = 23;
-	sharp->numchips = 1;
-	sharp->chips[0].start = 0;
-	sharp->chips[0].state = FL_READY;
-	sharp->chips[0].mutex = &sharp->chips[0]._spinlock;
-	sharp->chips[0].word_write_time = 0;
-	init_waitqueue_head(&sharp->chips[0].wq);
-	spin_lock_init(&sharp->chips[0]._spinlock);
-
-	map->fldrv = &sharp_chipdrv;
-	map->fldrv_priv = sharp;
-
-	__module_get(THIS_MODULE);
-	return mtd;
-}
-
-static inline void sharp_send_cmd(struct map_info *map, unsigned long cmd, unsigned long adr)
-{
-	map_word map_cmd;
-	map_cmd.x[0] = cmd;
-	map_write(map, map_cmd, adr);
-}
-
-static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd)
-{
-	map_word tmp, read0, read4;
-	unsigned long base = 0;
-	int width = 4;
-
-	tmp = map_read(map, base+0);
-
-	sharp_send_cmd(map, CMD_READ_ID, base+0);
-
-	read0 = map_read(map, base+0);
-	read4 = map_read(map, base+4);
-	if(read0.x[0] == 0x89898989){
-		printk("Looks like sharp flash\n");
-		switch(read4.x[0]){
-		case 0xaaaaaaaa:
-		case 0xa0a0a0a0:
-			/* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/
-			/* a0 - LH28F016SCT-Z4  2Mx8, 32 64k blocks*/
-			mtd->erasesize = 0x10000 * width;
-			mtd->size = 0x200000 * width;
-			return width;
-		case 0xa6a6a6a6:
-			/* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/
-			/* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/
-			mtd->erasesize = 0x10000 * width;
-			mtd->size = 0x100000 * width;
-			return width;
-#if 0
-		case 0x00000000: /* unknown */
-			/* XX - LH28F004SCT 512kx8, 8 64k blocks*/
-			mtd->erasesize = 0x10000 * width;
-			mtd->size = 0x80000 * width;
-			return width;
-#endif
-		default:
-			printk("Sort-of looks like sharp flash, 0x%08lx 0x%08lx\n",
-				read0.x[0], read4.x[0]);
-		}
-	}else if((map_read(map, base+0).x[0] == CMD_READ_ID)){
-		/* RAM, probably */
-		printk("Looks like RAM\n");
-		map_write(map, tmp, base+0);
-	}else{
-		printk("Doesn't look like sharp flash, 0x%08lx 0x%08lx\n",
-			read0.x[0], read4.x[0]);
-	}
-
-	return 0;
-}
-
-/* This function returns with the chip->mutex lock held. */
-static int sharp_wait(struct map_info *map, struct flchip *chip)
-{
-	int i;
-	map_word status;
-	unsigned long timeo = jiffies + HZ;
-	DECLARE_WAITQUEUE(wait, current);
-	int adr = 0;
-
-retry:
-	spin_lock_bh(chip->mutex);
-
-	switch(chip->state){
-	case FL_READY:
-		sharp_send_cmd(map, CMD_READ_STATUS, adr);
-		chip->state = FL_STATUS;
-	case FL_STATUS:
-		for(i=0;i<100;i++){
-			status = map_read(map, adr);
-			if((status.x[0] & SR_READY)==SR_READY)
-				break;
-			udelay(1);
-		}
-		break;
-	default:
-		printk("Waiting for chip\n");
-
-		set_current_state(TASK_INTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-
-		spin_unlock_bh(chip->mutex);
-
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-
-		if(signal_pending(current))
-			return -EINTR;
-
-		timeo = jiffies + HZ;
-
-		goto retry;
-	}
-
-	sharp_send_cmd(map, CMD_RESET, adr);
-
-	chip->state = FL_READY;
-
-	return 0;
-}
-
-static void sharp_release(struct flchip *chip)
-{
-	wake_up(&chip->wq);
-	spin_unlock_bh(chip->mutex);
-}
-
-static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
-	size_t *retlen, u_char *buf)
-{
-	struct map_info *map = mtd->priv;
-	struct sharp_info *sharp = map->fldrv_priv;
-	int chipnum;
-	int ret = 0;
-	int ofs = 0;
-
-	chipnum = (from >> sharp->chipshift);
-	ofs = from & ((1 << sharp->chipshift)-1);
-
-	*retlen = 0;
-
-	while(len){
-		unsigned long thislen;
-
-		if(chipnum>=sharp->numchips)
-			break;
-
-		thislen = len;
-		if(ofs+thislen >= (1<<sharp->chipshift))
-			thislen = (1<<sharp->chipshift) - ofs;
-
-		ret = sharp_wait(map,&sharp->chips[chipnum]);
-		if(ret<0)
-			break;
-
-		map_copy_from(map,buf,ofs,thislen);
-
-		sharp_release(&sharp->chips[chipnum]);
-
-		*retlen += thislen;
-		len -= thislen;
-		buf += thislen;
-
-		ofs = 0;
-		chipnum++;
-	}
-	return ret;
-}
-
-static int sharp_write(struct mtd_info *mtd, loff_t to, size_t len,
-	size_t *retlen, const u_char *buf)
-{
-	struct map_info *map = mtd->priv;
-	struct sharp_info *sharp = map->fldrv_priv;
-	int ret = 0;
-	int i,j;
-	int chipnum;
-	unsigned long ofs;
-	union { u32 l; unsigned char uc[4]; } tbuf;
-
-	*retlen = 0;
-
-	while(len){
-		tbuf.l = 0xffffffff;
-		chipnum = to >> sharp->chipshift;
-		ofs = to & ((1<<sharp->chipshift)-1);
-
-		j=0;
-		for(i=ofs&3;i<4 && len;i++){
-			tbuf.uc[i] = *buf;
-			buf++;
-			to++;
-			len--;
-			j++;
-		}
-		sharp_write_oneword(map, &sharp->chips[chipnum], ofs&~3, tbuf.l);
-		if(ret<0)
-			return ret;
-		(*retlen)+=j;
-	}
-
-	return 0;
-}
-
-static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
-	unsigned long adr, __u32 datum)
-{
-	int ret;
-	int timeo;
-	int try;
-	int i;
-	map_word data, status;
-
-	status.x[0] = 0;
-	ret = sharp_wait(map,chip);
-
-	for(try=0;try<10;try++){
-		sharp_send_cmd(map, CMD_BYTE_WRITE, adr);
-		/* cpu_to_le32 -> hack to fix the writel be->le conversion */
-		data.x[0] = cpu_to_le32(datum);
-		map_write(map, data, adr);
-
-		chip->state = FL_WRITING;
-
-		timeo = jiffies + (HZ/2);
-
-		sharp_send_cmd(map, CMD_READ_STATUS, adr);
-		for(i=0;i<100;i++){
-			status = map_read(map, adr);
-			if((status.x[0] & SR_READY) == SR_READY)
-				break;
-		}
-		if(i==100){
-			printk("sharp: timed out writing\n");
-		}
-
-		if(!(status.x[0] & SR_ERRORS))
-			break;
-
-		printk("sharp: error writing byte at addr=%08lx status=%08lx\n", adr, status.x[0]);
-
-		sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
-	}
-	sharp_send_cmd(map, CMD_RESET, adr);
-	chip->state = FL_READY;
-
-	wake_up(&chip->wq);
-	spin_unlock_bh(chip->mutex);
-
-	return 0;
-}
-
-static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr)
-{
-	struct map_info *map = mtd->priv;
-	struct sharp_info *sharp = map->fldrv_priv;
-	unsigned long adr,len;
-	int chipnum, ret=0;
-
-//printk("sharp_erase()\n");
-	if(instr->addr & (mtd->erasesize - 1))
-		return -EINVAL;
-	if(instr->len & (mtd->erasesize - 1))
-		return -EINVAL;
-	if(instr->len + instr->addr > mtd->size)
-		return -EINVAL;
-
-	chipnum = instr->addr >> sharp->chipshift;
-	adr = instr->addr & ((1<<sharp->chipshift)-1);
-	len = instr->len;
-
-	while(len){
-		ret = sharp_erase_oneblock(map, &sharp->chips[chipnum], adr);
-		if(ret)return ret;
-
-		adr += mtd->erasesize;
-		len -= mtd->erasesize;
-		if(adr >> sharp->chipshift){
-			adr = 0;
-			chipnum++;
-			if(chipnum>=sharp->numchips)
-				break;
-		}
-	}
-
-	instr->state = MTD_ERASE_DONE;
-	mtd_erase_callback(instr);
-
-	return 0;
-}
-
-static int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip,
-	unsigned long adr)
-{
-	int ret;
-	unsigned long timeo;
-	map_word status;
-	DECLARE_WAITQUEUE(wait, current);
-
-	sharp_send_cmd(map, CMD_READ_STATUS, adr);
-	status = map_read(map, adr);
-
-	timeo = jiffies + HZ;
-
-	while(time_before(jiffies, timeo)){
-		sharp_send_cmd(map, CMD_READ_STATUS, adr);
-		status = map_read(map, adr);
-		if((status.x[0] & SR_READY)==SR_READY){
-			ret = 0;
-			goto out;
-		}
-		set_current_state(TASK_INTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-
-		//spin_unlock_bh(chip->mutex);
-
-		schedule_timeout(1);
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-
-		//spin_lock_bh(chip->mutex);
-
-		if (signal_pending(current)){
-			ret = -EINTR;
-			goto out;
-		}
-
-	}
-	ret = -ETIME;
-out:
-	return ret;
-}
-
-static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
-	unsigned long adr)
-{
-	int ret;
-	//int timeo;
-	map_word status;
-	//int i;
-
-//printk("sharp_erase_oneblock()\n");
-
-#ifdef AUTOUNLOCK
-	/* This seems like a good place to do an unlock */
-	sharp_unlock_oneblock(map,chip,adr);
-#endif
-
-	sharp_send_cmd(map, CMD_BLOCK_ERASE_1, adr);
-	sharp_send_cmd(map, CMD_BLOCK_ERASE_2, adr);
-
-	chip->state = FL_ERASING;
-
-	ret = sharp_do_wait_for_ready(map,chip,adr);
-	if(ret<0)return ret;
-
-	sharp_send_cmd(map, CMD_READ_STATUS, adr);
-	status = map_read(map, adr);
-
-	if(!(status.x[0] & SR_ERRORS)){
-		sharp_send_cmd(map, CMD_RESET, adr);
-		chip->state = FL_READY;
-		//spin_unlock_bh(chip->mutex);
-		return 0;
-	}
-
-	printk("sharp: error erasing block at addr=%08lx status=%08lx\n", adr, status.x[0]);
-	sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
-
-	//spin_unlock_bh(chip->mutex);
-
-	return -EIO;
-}
-
-#ifdef AUTOUNLOCK
-static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
-	unsigned long adr)
-{
-	int i;
-	map_word status;
-
-	sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_1, adr);
-	sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_2, adr);
-
-	udelay(100);
-
-	status = map_read(map, adr);
-	printk("status=%08lx\n", status.x[0]);
-
-	for(i=0;i<1000;i++){
-		//sharp_send_cmd(map, CMD_READ_STATUS, adr);
-		status = map_read(map, adr);
-		if((status.x[0] & SR_READY) == SR_READY)
-			break;
-		udelay(100);
-	}
-	if(i==1000){
-		printk("sharp: timed out unlocking block\n");
-	}
-
-	if(!(status.x[0] & SR_ERRORS)){
-		sharp_send_cmd(map, CMD_RESET, adr);
-		chip->state = FL_READY;
-		return;
-	}
-
-	printk("sharp: error unlocking block at addr=%08lx status=%08lx\n", adr, status.x[0]);
-	sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
-}
-#endif
-
-static void sharp_sync(struct mtd_info *mtd)
-{
-	//printk("sharp_sync()\n");
-}
-
-static int sharp_suspend(struct mtd_info *mtd)
-{
-	printk("sharp_suspend()\n");
-	return -EINVAL;
-}
-
-static void sharp_resume(struct mtd_info *mtd)
-{
-	printk("sharp_resume()\n");
-
-}
-
-static void sharp_destroy(struct mtd_info *mtd)
-{
-	printk("sharp_destroy()\n");
-
-}
-
-static int __init sharp_probe_init(void)
-{
-	printk("MTD Sharp chip driver <ds at lineo.com>\n");
-
-	register_mtd_chip_driver(&sharp_chipdrv);
-
-	return 0;
-}
-
-static void __exit sharp_probe_exit(void)
-{
-	unregister_mtd_chip_driver(&sharp_chipdrv);
-}
-
-module_init(sharp_probe_init);
-module_exit(sharp_probe_exit);
-
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Schleef <ds at schleef.org>");
-MODULE_DESCRIPTION("Old MTD chip driver for pre-CFI Sharp flash chips");


From linux-mtd at lists.infradead.org  Sun May 20 08:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Sun, 20 May 2007 08:59:01 -0400
Subject: [MTD] [MAPS] don't force uclinux mtd map to be root dev
Message-ID: <E1Hpl01-0001Ke-Pr@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=33672f72b24191fb17c3666ec1c620f3fa617b50
Commit:     33672f72b24191fb17c3666ec1c620f3fa617b50
Parent:     b7aa48be1e7a11e36448a7db58931bbf735d2718
Author:     Mike Frysinger <vapier at gentoo.org>
AuthorDate: Thu May 10 22:51:53 2007 -0700
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Fri May 11 11:48:15 2007 +0100

    [MTD] [MAPS] don't force uclinux mtd map to be root dev
    
    The cheesy uclinux mtd maps can be used for more than just the root device, so
    I think we should drop the forcing.
    
    Also, I feel like this is a policy decision that shouldnt be in the kernel in
    the first place.  People who have been lazy and boot with uclinux mtd maps and
    dont put root= into their commandline can simply add the appropriate root=
    line either into their bootloader or into the compiled in bootargs.
    
    Signed-off-by: Mike Frysinger <vapier at gentoo.org>
    Acked-by: Christoph Hellwig <hch at lst.de>
    Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/maps/uclinux.c |    5 -----
 1 files changed, 0 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c
index 389fea2..14ffb1a 100644
--- a/drivers/mtd/maps/uclinux.c
+++ b/drivers/mtd/maps/uclinux.c
@@ -16,7 +16,6 @@
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/major.h>
-#include <linux/root_dev.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
@@ -89,10 +88,6 @@ int __init uclinux_mtd_init(void)
 	uclinux_ram_mtdinfo = mtd;
 	add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS);
 
-	printk("uclinux[mtd]: set %s to be root filesystem\n",
-	     	uclinux_romfs[0].name);
-	ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 0);
-
 	return(0);
 }
 


From linux-mtd at lists.infradead.org  Sun May 20 08:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Sun, 20 May 2007 08:59:01 -0400
Subject: [MTD] generalise the handling of MTD-specific superblocks
Message-ID: <E1Hpl01-0001Kz-Td@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=acaebfd8a7af0019b2edfcf4045c56c3e18375c5
Commit:     acaebfd8a7af0019b2edfcf4045c56c3e18375c5
Parent:     33672f72b24191fb17c3666ec1c620f3fa617b50
Author:     David Howells <dhowells at redhat.com>
AuthorDate: Thu May 10 22:51:50 2007 -0700
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Fri May 11 12:14:15 2007 +0100

    [MTD] generalise the handling of MTD-specific superblocks
    
    Generalise the handling of MTD-specific superblocks so that JFFS2 and ROMFS
    can both share it.
    
    Signed-off-by: David Howells <dhowells at redhat.com>
    Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/mtd/Makefile      |    3 +-
 drivers/mtd/mtdsuper.c    |  232 +++++++++++++++++++++++++++++++++++++++++++++
 fs/jffs2/super.c          |  194 ++++----------------------------------
 include/linux/fs.h        |    1 +
 include/linux/mtd/super.h |   30 ++++++
 5 files changed, 283 insertions(+), 177 deletions(-)

diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 9205540..451adcc 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -1,10 +1,9 @@
 #
 # Makefile for the memory technology device drivers.
 #
-# $Id: Makefile.common,v 1.7 2005/07/11 10:39:27 gleixner Exp $
 
 # Core functionality.
-mtd-y				:= mtdcore.o
+mtd-y				:= mtdcore.o mtdsuper.o
 mtd-$(CONFIG_MTD_PARTITIONS)	+= mtdpart.o
 obj-$(CONFIG_MTD)		+= $(mtd-y)
 
diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c
new file mode 100644
index 0000000..a279540
--- /dev/null
+++ b/drivers/mtd/mtdsuper.c
@@ -0,0 +1,232 @@
+/* MTD-based superblock management
+ *
+ * Copyright ? 2001-2007 Red Hat, Inc. All Rights Reserved.
+ * Written by:  David Howells <dhowells at redhat.com>
+ *              David Woodhouse <dwmw2 at infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/mtd/super.h>
+#include <linux/namei.h>
+#include <linux/ctype.h>
+
+/*
+ * compare superblocks to see if they're equivalent
+ * - they are if the underlying MTD device is the same
+ */
+static int get_sb_mtd_compare(struct super_block *sb, void *_mtd)
+{
+	struct mtd_info *mtd = _mtd;
+
+	if (sb->s_mtd == mtd) {
+		DEBUG(2, "MTDSB: Match on device %d (\"%s\")\n",
+		      mtd->index, mtd->name);
+		return 1;
+	}
+
+	DEBUG(2, "MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
+	      sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
+	return 0;
+}
+
+/*
+ * mark the superblock by the MTD device it is using
+ * - set the device number to be the correct MTD block device for pesuperstence
+ *   of NFS exports
+ */
+static int get_sb_mtd_set(struct super_block *sb, void *_mtd)
+{
+	struct mtd_info *mtd = _mtd;
+
+	sb->s_mtd = mtd;
+	sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
+	return 0;
+}
+
+/*
+ * get a superblock on an MTD-backed filesystem
+ */
+static int get_sb_mtd_aux(struct file_system_type *fs_type, int flags,
+			  const char *dev_name, void *data,
+			  struct mtd_info *mtd,
+			  int (*fill_super)(struct super_block *, void *, int),
+			  struct vfsmount *mnt)
+{
+	struct super_block *sb;
+	int ret;
+
+	sb = sget(fs_type, get_sb_mtd_compare, get_sb_mtd_set, mtd);
+	if (IS_ERR(sb))
+		goto out_error;
+
+	if (sb->s_root)
+		goto already_mounted;
+
+	/* fresh new superblock */
+	DEBUG(1, "MTDSB: New superblock for device %d (\"%s\")\n",
+	      mtd->index, mtd->name);
+
+	ret = fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
+	if (ret < 0) {
+		up_write(&sb->s_umount);
+		deactivate_super(sb);
+		return ret;
+	}
+
+	/* go */
+	sb->s_flags |= MS_ACTIVE;
+	return simple_set_mnt(mnt, sb);
+
+	/* new mountpoint for an already mounted superblock */
+already_mounted:
+	DEBUG(1, "MTDSB: Device %d (\"%s\") is already mounted\n",
+	      mtd->index, mtd->name);
+	ret = simple_set_mnt(mnt, sb);
+	goto out_put;
+
+out_error:
+	ret = PTR_ERR(sb);
+out_put:
+	put_mtd_device(mtd);
+	return ret;
+}
+
+/*
+ * get a superblock on an MTD-backed filesystem by MTD device number
+ */
+static int get_sb_mtd_nr(struct file_system_type *fs_type, int flags,
+			 const char *dev_name, void *data, int mtdnr,
+			 int (*fill_super)(struct super_block *, void *, int),
+			 struct vfsmount *mnt)
+{
+	struct mtd_info *mtd;
+
+	mtd = get_mtd_device(NULL, mtdnr);
+	if (!mtd) {
+		DEBUG(0, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
+		return -EINVAL;
+	}
+
+	return get_sb_mtd_aux(fs_type, flags, dev_name, data, mtd, fill_super,
+			      mnt);
+}
+
+/*
+ * set up an MTD-based superblock
+ */
+int get_sb_mtd(struct file_system_type *fs_type, int flags,
+	       const char *dev_name, void *data,
+	       int (*fill_super)(struct super_block *, void *, int),
+	       struct vfsmount *mnt)
+{
+	struct nameidata nd;
+	int mtdnr, ret;
+
+	if (!dev_name)
+		return -EINVAL;
+
+	DEBUG(2, "MTDSB: dev_name \"%s\"\n", dev_name);
+
+	/* the preferred way of mounting in future; especially when
+	 * CONFIG_BLOCK=n - we specify the underlying MTD device by number or
+	 * by name, so that we don't require block device support to be present
+	 * in the kernel. */
+	if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
+		if (dev_name[3] == ':') {
+			struct mtd_info *mtd;
+
+			/* mount by MTD device name */
+			DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n",
+			      dev_name + 4);
+
+			for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
+				mtd = get_mtd_device(NULL, mtdnr);
+				if (mtd) {
+					if (!strcmp(mtd->name, dev_name + 4))
+						return get_sb_mtd_aux(
+							fs_type, flags,
+							dev_name, data, mtd,
+							fill_super, mnt);
+
+					put_mtd_device(mtd);
+				}
+			}
+
+			printk(KERN_NOTICE "MTD:"
+			       " MTD device with name \"%s\" not found.\n",
+			       dev_name + 4);
+
+		} else if (isdigit(dev_name[3])) {
+			/* mount by MTD device number name */
+			char *endptr;
+
+			mtdnr = simple_strtoul(dev_name + 3, &endptr, 0);
+			if (!*endptr) {
+				/* It was a valid number */
+				DEBUG(1, "MTDSB: mtd%%d, mtdnr %d\n",
+				      mtdnr);
+				return get_sb_mtd_nr(fs_type, flags,
+						     dev_name, data,
+						     mtdnr, fill_super, mnt);
+			}
+		}
+	}
+
+	/* try the old way - the hack where we allowed users to mount
+	 * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
+	 */
+	ret = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
+
+	DEBUG(1, "MTDSB: path_lookup() returned %d, inode %p\n",
+	      ret, nd.dentry ? nd.dentry->d_inode : NULL);
+
+	if (ret)
+		return ret;
+
+	ret = -EINVAL;
+
+	if (!S_ISBLK(nd.dentry->d_inode->i_mode))
+		goto out;
+
+	if (nd.mnt->mnt_flags & MNT_NODEV) {
+		ret = -EACCES;
+		goto out;
+	}
+
+	if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR)
+		goto not_an_MTD_device;
+
+	mtdnr = iminor(nd.dentry->d_inode);
+	path_release(&nd);
+
+	return get_sb_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super,
+			     mnt);
+
+not_an_MTD_device:
+	if (!(flags & MS_SILENT))
+		printk(KERN_NOTICE
+		       "MTD: Attempt to mount non-MTD device \"%s\"\n",
+		       dev_name);
+out:
+	path_release(&nd);
+	return ret;
+
+}
+
+EXPORT_SYMBOL_GPL(get_sb_mtd);
+
+/*
+ * destroy an MTD-based superblock
+ */
+void kill_mtd_super(struct super_block *sb)
+{
+	generic_shutdown_super(sb);
+	put_mtd_device(sb->s_mtd);
+	sb->s_mtd = NULL;
+}
+
+EXPORT_SYMBOL_GPL(kill_mtd_super);
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index e51164a..e3c69e6 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -19,7 +19,7 @@
 #include <linux/mount.h>
 #include <linux/jffs2.h>
 #include <linux/pagemap.h>
-#include <linux/mtd/mtd.h>
+#include <linux/mtd/super.h>
 #include <linux/ctype.h>
 #include <linux/namei.h>
 #include "compr.h"
@@ -78,69 +78,27 @@ static const struct super_operations jffs2_super_operations =
 	.sync_fs =	jffs2_sync_fs,
 };
 
-static int jffs2_sb_compare(struct super_block *sb, void *data)
-{
-	struct jffs2_sb_info *p = data;
-	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-
-	/* The superblocks are considered to be equivalent if the underlying MTD
-	   device is the same one */
-	if (c->mtd == p->mtd) {
-		D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name));
-		return 1;
-	} else {
-		D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n",
-			  c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name));
-		return 0;
-	}
-}
-
-static int jffs2_sb_set(struct super_block *sb, void *data)
-{
-	struct jffs2_sb_info *p = data;
-
-	/* For persistence of NFS exports etc. we use the same s_dev
-	   each time we mount the device, don't just use an anonymous
-	   device */
-	sb->s_fs_info = p;
-	p->os_priv = sb;
-	sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index);
-
-	return 0;
-}
-
-static int jffs2_get_sb_mtd(struct file_system_type *fs_type,
-			    int flags, const char *dev_name,
-			    void *data, struct mtd_info *mtd,
-			    struct vfsmount *mnt)
+/*
+ * fill in the superblock
+ */
+static int jffs2_fill_super(struct super_block *sb, void *data, int silent)
 {
-	struct super_block *sb;
 	struct jffs2_sb_info *c;
-	int ret;
+
+	D1(printk(KERN_DEBUG "jffs2_get_sb_mtd():"
+		  " New superblock for device %d (\"%s\")\n",
+		  sb->s_mtd->index, sb->s_mtd->name));
 
 	c = kzalloc(sizeof(*c), GFP_KERNEL);
 	if (!c)
 		return -ENOMEM;
-	c->mtd = mtd;
-
-	sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
-
-	if (IS_ERR(sb))
-		goto out_error;
-
-	if (sb->s_root) {
-		/* New mountpoint for JFFS2 which is already mounted */
-		D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n",
-			  mtd->index, mtd->name));
-		ret = simple_set_mnt(mnt, sb);
-		goto out_put;
-	}
 
-	D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
-		  mtd->index, mtd->name));
+	c->mtd = sb->s_mtd;
+	c->os_priv = sb;
+	sb->s_fs_info = c;
 
-	/* Initialize JFFS2 superblock locks, the further initialization will be
-	 * done later */
+	/* Initialize JFFS2 superblock locks, the further initialization will
+	 * be done later */
 	init_MUTEX(&c->alloc_sem);
 	init_MUTEX(&c->erase_free_sem);
 	init_waitqueue_head(&c->erase_wait);
@@ -149,133 +107,20 @@ static int jffs2_get_sb_mtd(struct file_system_type *fs_type,
 	spin_lock_init(&c->inocache_lock);
 
 	sb->s_op = &jffs2_super_operations;
-	sb->s_flags = flags | MS_NOATIME;
+	sb->s_flags = sb->s_flags | MS_NOATIME;
 	sb->s_xattr = jffs2_xattr_handlers;
 #ifdef CONFIG_JFFS2_FS_POSIX_ACL
 	sb->s_flags |= MS_POSIXACL;
 #endif
-	ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
-
-	if (ret) {
-		/* Failure case... */
-		up_write(&sb->s_umount);
-		deactivate_super(sb);
-		return ret;
-	}
-
-	sb->s_flags |= MS_ACTIVE;
-	return simple_set_mnt(mnt, sb);
-
-out_error:
-	ret = PTR_ERR(sb);
- out_put:
-	kfree(c);
-	put_mtd_device(mtd);
-
-	return ret;
-}
-
-static int jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
-			      int flags, const char *dev_name,
-			      void *data, int mtdnr,
-			      struct vfsmount *mnt)
-{
-	struct mtd_info *mtd;
-
-	mtd = get_mtd_device(NULL, mtdnr);
-	if (IS_ERR(mtd)) {
-		D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
-		return PTR_ERR(mtd);
-	}
-
-	return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
+	return jffs2_do_fill_super(sb, data, silent);
 }
 
 static int jffs2_get_sb(struct file_system_type *fs_type,
 			int flags, const char *dev_name,
 			void *data, struct vfsmount *mnt)
 {
-	int err;
-	struct nameidata nd;
-	int mtdnr;
-
-	if (!dev_name)
-		return -EINVAL;
-
-	D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
-
-	/* The preferred way of mounting in future; especially when
-	   CONFIG_BLK_DEV is implemented - we specify the underlying
-	   MTD device by number or by name, so that we don't require
-	   block device support to be present in the kernel. */
-
-	/* FIXME: How to do the root fs this way? */
-
-	if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
-		/* Probably mounting without the blkdev crap */
-		if (dev_name[3] == ':') {
-			struct mtd_info *mtd;
-
-			/* Mount by MTD device name */
-			D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
-			for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
-				mtd = get_mtd_device(NULL, mtdnr);
-				if (!IS_ERR(mtd)) {
-					if (!strcmp(mtd->name, dev_name+4))
-						return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt);
-					put_mtd_device(mtd);
-				}
-			}
-			printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4);
-		} else if (isdigit(dev_name[3])) {
-			/* Mount by MTD device number name */
-			char *endptr;
-
-			mtdnr = simple_strtoul(dev_name+3, &endptr, 0);
-			if (!*endptr) {
-				/* It was a valid number */
-				D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr));
-				return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt);
-			}
-		}
-	}
-
-	/* Try the old way - the hack where we allowed users to mount
-	   /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
-
-	err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
-
-	D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
-		  err, nd.dentry->d_inode));
-
-	if (err)
-		return err;
-
-	err = -EINVAL;
-
-	if (!S_ISBLK(nd.dentry->d_inode->i_mode))
-		goto out;
-
-	if (nd.mnt->mnt_flags & MNT_NODEV) {
-		err = -EACCES;
-		goto out;
-	}
-
-	if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) {
-		if (!(flags & MS_SILENT))
-			printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n",
-			       dev_name);
-		goto out;
-	}
-
-	mtdnr = iminor(nd.dentry->d_inode);
-	path_release(&nd);
-
-	return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt);
-
-out:
-	path_release(&nd);
-	return err;
+	return get_sb_mtd(fs_type, flags, dev_name, data, jffs2_fill_super,
+			  mnt);
 }
 
 static void jffs2_put_super (struct super_block *sb)
@@ -310,8 +155,7 @@ static void jffs2_kill_sb(struct super_block *sb)
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 	if (!(sb->s_flags & MS_RDONLY))
 		jffs2_stop_garbage_collect_thread(c);
-	generic_shutdown_super(sb);
-	put_mtd_device(c->mtd);
+	kill_mtd_super(sb);
 	kfree(c);
 }
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 095a9c9..151739a 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -937,6 +937,7 @@ struct super_block {
 	struct list_head	s_files;
 
 	struct block_device	*s_bdev;
+	struct mtd_info		*s_mtd;
 	struct list_head	s_instances;
 	struct quota_info	s_dquot;	/* Diskquota specific options */
 
diff --git a/include/linux/mtd/super.h b/include/linux/mtd/super.h
new file mode 100644
index 0000000..4016dd6
--- /dev/null
+++ b/include/linux/mtd/super.h
@@ -0,0 +1,30 @@
+/* MTD-based superblock handling
+ *
+ * Copyright ? 2006 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef __MTD_SUPER_H__
+#define __MTD_SUPER_H__
+
+#ifdef __KERNEL__
+
+#include <linux/mtd/mtd.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+
+extern int get_sb_mtd(struct file_system_type *fs_type, int flags,
+		      const char *dev_name, void *data,
+		      int (*fill_super)(struct super_block *, void *, int),
+		      struct vfsmount *mnt);
+extern void kill_mtd_super(struct super_block *sb);
+
+
+#endif /* __KERNEL__ */
+
+#endif /* __MTD_SUPER_H__ */


From linux-mtd at lists.infradead.org  Mon May 21 11:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Mon, 21 May 2007 11:59:01 -0400
Subject: [JFFS2] Fix BUG() caused by failing to discard xattrs on deleted
	files.
Message-ID: <E1HqAHl-0007KH-DJ@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=8ae5d31263c746f1680d005b33a82d167cdb9eb6
Commit:     8ae5d31263c746f1680d005b33a82d167cdb9eb6
Parent:     acaebfd8a7af0019b2edfcf4045c56c3e18375c5
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Sat May 5 17:50:25 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Sun May 20 11:28:22 2007 -0400

    [JFFS2] Fix BUG() caused by failing to discard xattrs on deleted files.
    
    When we cannot mark nodes as obsolete, such as on NAND flash, we end up
    having to delete inodes with !nlink in jffs2_build_remove_unlinked_inode().
    However, jffs2_build_xattr_subsystem() runs later than this, and will
    attach an xref to the dead inode. Then later when the last nodes of that
    dead inode are erased we hit a BUG() in jffs2_del_ino_cache()
    because we're not supposed to get there with an xattr still attached to
    the inode which is being killed.
    
    The simple fix is to refrain from attaching xattrs to inodes with zero
    nlink, in jffs2_build_xattr_subsystem(). It's it's OK to trust nlink
    here because the file system isn't actually mounted yet, so there's no
    chance that a zero-nlink file could actually be alive still because
    it's open.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 fs/jffs2/xattr.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c
index 78fc088..0734698 100644
--- a/fs/jffs2/xattr.c
+++ b/fs/jffs2/xattr.c
@@ -825,7 +825,7 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
 			   ref->xd and ref->ic are not valid yet. */
 			xd = jffs2_find_xattr_datum(c, ref->xid);
 			ic = jffs2_get_ino_cache(c, ref->ino);
-			if (!xd || !ic) {
+			if (!xd || !ic || !ic->nlink) {
 				dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) is orphan.\n",
 					  ref->ino, ref->xid, ref->xseqno);
 				ref->xseqno |= XREF_DELETE_MARKER;


From linux-mtd at lists.infradead.org  Mon May 21 11:59:01 2007
From: linux-mtd at lists.infradead.org (Linux-MTD Mailing List)
Date: Mon, 21 May 2007 11:59:01 -0400
Subject: [JFFS2] Fix potential memory leak of dead xattrs on unmount.
Message-ID: <E1HqAHl-0007Kc-FR@bombadil.infradead.org>

Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=2ad8ee713566671875216ebcec64f2eda47bd19d
Commit:     2ad8ee713566671875216ebcec64f2eda47bd19d
Parent:     8ae5d31263c746f1680d005b33a82d167cdb9eb6
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Tue May 8 00:12:58 2007 +0100
Committer:  David Woodhouse <dwmw2 at infradead.org>
CommitDate: Sun May 20 11:30:38 2007 -0400

    [JFFS2] Fix potential memory leak of dead xattrs on unmount.
    
    An xattr_datum which ends up orphaned should be freed by the GC
    thread. But if we umount before the GC thread is finished, or if we
    mount read-only and the GC thread never runs, they might never be
    freed. Clean them up during unmount, if there are any left.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 fs/jffs2/xattr.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c
index 0734698..e486659 100644
--- a/fs/jffs2/xattr.c
+++ b/fs/jffs2/xattr.c
@@ -754,6 +754,10 @@ void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c)
 		list_del(&xd->xindex);
 		jffs2_free_xattr_datum(xd);
 	}
+	list_for_each_entry_safe(xd, _xd, &c->xattr_unchecked, xindex) {
+		list_del(&xd->xindex);
+		jffs2_free_xattr_datum(xd);
+	}
 }
 
 #define XREF_TMPHASH_SIZE	(128)


