diff -x asm -Naur linux-2.4.30-orig/arch/mips/Makefile linux-2.4.30/arch/mips/Makefile
--- linux-2.4.30-orig/arch/mips/Makefile	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/arch/mips/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -17,8 +17,8 @@
 # Select the object file format to substitute into the linker script.
 #
 ifdef CONFIG_CPU_LITTLE_ENDIAN
-#tool-prefix	= mipsel-linux-
-tool-prefix	= mips_nfp_le-
+tool-prefix     = mipsel-linux-
+#tool-prefix	= mips_nfp_le-
 ld-emul		= elf32ltsmip
 else
 tool-prefix	= mips-linux-
@@ -258,6 +258,13 @@
 LOADADDR      += 0x80100000
 endif
 
+ifdef CONFIG_MIPS_I3_MMB3
+LIBS          += arch/mips/au1000/db1x00/db1x00.o \
+                 arch/mips/au1000/common/au1000.o
+SUBDIRS       += arch/mips/au1000/db1x00 arch/mips/au1000/common
+LOADADDR      += 0x80100000
+endif
+
 ifdef CONFIG_MIPS_DB1100
 LIBS          += arch/mips/au1000/db1x00/db1x00.o \
                  arch/mips/au1000/common/au1000.o
diff -x asm -Naur linux-2.4.30-orig/arch/mips/au1000/common/dbdma.c linux-2.4.30/arch/mips/au1000/common/dbdma.c
--- linux-2.4.30-orig/arch/mips/au1000/common/dbdma.c	2005-05-12 17:57:05.000000000 +0200
+++ linux-2.4.30/arch/mips/au1000/common/dbdma.c	2006-12-11 18:18:37.000000000 +0100
@@ -209,10 +209,6 @@
  		p->dev_id = DSCR_DEV2CUSTOM_ID(new_id,dev->dev_id);
 		ret = p->dev_id;
 		new_id++;
-#if 0
-		printk("add_device: id:%x flags:%x padd:%x\n", 
-				p->dev_id, p->dev_flags, p->dev_physaddr );
-#endif
 	}
 
 	return ret;
@@ -316,6 +312,10 @@
 			if ((stp->dev_flags & DEV_FLAGS_SYNC) ||
 				(dtp->dev_flags & DEV_FLAGS_SYNC))
 					i |= DDMA_CFG_SYNC;
+#if defined(CONFIG_NONCOHERENT_IO)
+                        // DBDMA reads descriptors non-coherently
+                        i |= DDMA_CFG_DFN;
+#endif
 			cp->ddma_cfg = i;
 			au_sync();
 
@@ -337,9 +337,9 @@
 
 /* Set the device width if source or destination is a FIFO.
  * Should be 8, 16, or 32 bits.
+ * The membits is added. If = 0 => no change.
  */
-u32
-au1xxx_dbdma_set_devwidth(u32 chanid, int bits)
+u32 _au1xxx_dbdma_set_devwidth(u32 chanid, u32 iobits, u32 membits)
 {
 	u32		rv;
 	chan_tab_t	*ctp;
@@ -352,16 +352,45 @@
 
 	if (stp->dev_flags & DEV_FLAGS_IN) {	/* Source in fifo */
 		rv = stp->dev_devwidth;
-		stp->dev_devwidth = bits;
+		stp->dev_devwidth = iobits;
+		if (membits)
+		  dtp->dev_devwidth = membits;
 	}
 	if (dtp->dev_flags & DEV_FLAGS_OUT) {	/* Destination out fifo */
 		rv = dtp->dev_devwidth;
-		dtp->dev_devwidth = bits;
+		dtp->dev_devwidth = iobits;
+		if (membits)
+		  stp->dev_devwidth = membits;
 	}
 
 	return rv;
 }
-EXPORT_SYMBOL(au1xxx_dbdma_set_devwidth);
+EXPORT_SYMBOL(_au1xxx_dbdma_set_devwidth);
+
+/* Set the transfer size of the data structure for src1 and dest1.
+ * return value = ((src_datums << 16) | dest_datums);
+ * This function should be called before au1xxx_dbdma_ring_alloc()
+ */
+u32
+_au1xxx_dbdma_set_transfer_size(u32 chanid, u32 src_datums, u32 dest_datums)
+{
+	u32		rv = 0;
+	chan_tab_t	*ctp;
+	dbdev_tab_t	*stp, *dtp;
+
+	ctp = *((chan_tab_t **)chanid);
+	if (ctp == NULL) return 0;
+
+	stp = ctp->chan_src;
+	dtp = ctp->chan_dest;
+
+	rv = stp->dev_tsize = src_datums;
+	dtp->dev_tsize = dest_datums;
+	rv <<= 16;
+	rv |= dest_datums;
+	return rv;	
+}
+EXPORT_SYMBOL(_au1xxx_dbdma_set_transfer_size);
 
 /* Allocate a descriptor ring, initializing as much as possible.
 */
@@ -375,6 +404,7 @@
 	chan_tab_t		*ctp;
 	dbdev_tab_t		*stp, *dtp;
 	au1x_ddma_desc_t	*dp;
+	au1x_dma_chan_t		*cp;
 
 	/* I guess we could check this to be within the
 	 * range of the table......
@@ -382,6 +412,7 @@
 	ctp = *((chan_tab_t **)chanid);
 	stp = ctp->chan_src;
 	dtp = ctp->chan_dest;
+	cp = ctp->chan_ptr;
 
 	/* The descriptors must be 32-byte aligned.  There is a
 	 * possibility the allocation will give us such an address,
@@ -422,7 +453,8 @@
 	cmd0 |= DSCR_CMD0_SID(srcid);
 	cmd0 |= DSCR_CMD0_DID(destid);
 	cmd0 |= DSCR_CMD0_IE | DSCR_CMD0_CV;
-	cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_CURRENT);
+	//	cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_CURRENT);
+	cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_NOCHANGE);
 
 	switch (stp->dev_devwidth) {
 	case 8:
@@ -458,6 +490,11 @@
 	if (dtp->dev_flags & DEV_FLAGS_OUT)
 		cmd0 |= DSCR_CMD0_DN;		/* Destination out fifo */
 
+#ifdef KC_HACK
+	/* set as higer priority */
+	cmd0 |= DSCR_CMD0_ARB;
+#endif
+
 	/* Set up source1.  For now, assume no stride and increment.
 	 * A channel attribute update can change this later.
 	 */
@@ -472,9 +509,11 @@
 		src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE4);
 		break;
 	case 8:
-	default:
 		src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE8);
 		break;
+	default:
+		src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE1);
+		break;
 	}
 
 	/* If source input is fifo, set static address.
@@ -503,9 +542,11 @@
 		dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE4);
 		break;
 	case 8:
-	default:
 		dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE8);
 		break;
+	default:
+		dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE1);
+		break;
 	}
 
 	/* If destination output is fifo, set static address.
@@ -513,16 +554,12 @@
 	if (dtp->dev_flags & DEV_FLAGS_OUT) {
 		if ( dtp->dev_flags & DEV_FLAGS_BURSTABLE )
 	                dest1 |= DSCR_DEST1_DAM(DSCR_xAM_BURST);
-				else
+		else
 		dest1 |= DSCR_DEST1_DAM(DSCR_xAM_STATIC);
 	}
 	if (dtp->dev_physaddr) 
 		dest0 = dtp->dev_physaddr;
 	
-#if 0
-		printk("did:%x sid:%x cmd0:%x cmd1:%x source0:%x source1:%x dest0:%x dest1:%x\n",
-			dtp->dev_id, stp->dev_id, cmd0, cmd1, src0, src1, dest0, dest1 );
-#endif
 	for (i=0; i<entries; i++) {
 		dp->dscr_cmd0 = cmd0;
 		dp->dscr_cmd1 = cmd1;
@@ -531,8 +568,11 @@
 		dp->dscr_dest0 = dest0;
 		dp->dscr_dest1 = dest1;
 		dp->dscr_stat = 0;
-				dp->sw_context = dp->sw_status = 0;
 		dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(dp + 1));
+#if defined(CONFIG_NONCOHERENT_IO)
+		au_sync();
+		dma_cache_wback_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
 		dp++;
 	}
 	
@@ -540,7 +580,14 @@
 	*/
 	dp--;
 	dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(ctp->chan_desc_base));
-	ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base;
+#if defined(CONFIG_NONCOHERENT_IO)
+	au_sync();
+	dma_cache_wback_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
+	ctp->ptr = ctp->chan_desc_base;
+	cp->ddma_desptr = virt_to_phys(ctp->chan_desc_base);
+
+	au_sync();
 
 	return (u32)(ctp->chan_desc_base);
 }
@@ -565,7 +612,10 @@
 	 * an interrupt doesn't affect this pointer nor the descriptor,
 	 * so no locking should be needed.
 	 */
-	dp = ctp->put_ptr;
+	dp = ctp->ptr;
+#if defined(CONFIG_NONCOHERENT_IO)
+	dma_cache_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
 
 	/* If the descriptor is valid, we are way ahead of the DMA
 	 * engine, so just return an error condition.
@@ -573,11 +623,13 @@
 	if (dp->dscr_cmd0 & DSCR_CMD0_V) {
 		return 0;
 	}
-	
+
 	/* Load up buffer address and byte count.
 	*/
 	dp->dscr_source0 = virt_to_phys(buf);
-	dp->dscr_cmd1 = nbytes;
+	dp->dscr_cmd1 &= ~(0x3fffff);
+	dp->dscr_cmd1 |= nbytes;
+
 	/* Check flags  */
 	if (flags & DDMA_FLAGS_IE) 
 		dp->dscr_cmd0 |= DSCR_CMD0_IE;
@@ -585,7 +637,7 @@
 		dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
 	/* Get next descriptor pointer.
 	*/
-	ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+	ctp->ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 
 	/*
 	 * There is an errata on the Au1200/Au1550 parts that could result 
@@ -594,11 +646,17 @@
 	 * these parts. If it is fixedin the future, these dma_cache_inv will 
 	 * just be nothing more than empty macros. See io.h.
 	 * */
-	dma_cache_wback_inv(buf,nbytes);
+	au_sync();
+#if defined(CONFIG_NONCOHERENT_IO)
+	dma_cache_wback_inv((unsigned long)buf, (unsigned long)nbytes);
+#endif
         dp->dscr_cmd0 |= DSCR_CMD0_V;        /* Let it rip */
+#if defined(CONFIG_NONCOHERENT_IO)
 	au_sync();
-	dma_cache_wback_inv(dp, sizeof(dp));
+	dma_cache_wback_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
         ctp->chan_ptr->ddma_dbell = 0;
+	au_sync();
 
 	/* return something not zero.
 	*/
@@ -625,7 +683,10 @@
 	 * an interrupt doesn't affect this pointer nor the descriptor,
 	 * so no locking should be needed.
 	 */
-	dp = ctp->put_ptr;
+	dp = ctp->ptr;
+#if defined(CONFIG_NONCOHERENT_IO)
+	dma_cache_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
 
 	/* If the descriptor is valid, we are way ahead of the DMA
 	 * engine, so just return an error condition.
@@ -642,12 +703,9 @@
 		dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
 
 	dp->dscr_dest0 = virt_to_phys(buf);
-	dp->dscr_cmd1 = nbytes;
-#if 0
-	printk("cmd0:%x cmd1:%x source0:%x source1:%x dest0:%x dest1:%x\n", 
-			dp->dscr_cmd0, dp->dscr_cmd1, dp->dscr_source0, 
-			dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1 );
-#endif
+	dp->dscr_cmd1 &= ~(0x3fffff);
+	dp->dscr_cmd1 |= nbytes;
+
 	/*
 	 * There is an errata on the Au1200/Au1550 parts that could result in 
 	 * "stale" data being DMA'd. It has to do with the snoop logic on the 
@@ -655,15 +713,22 @@
 	 * parts. If it is fixedin the future, these dma_cache_inv will just 
 	 * be nothing more than empty macros. See io.h. 
 	 * */
-	dma_cache_inv(buf,nbytes);
+#if defined(CONFIG_NONCOHERENT_IO)
+        // Need to do a wback since there may be other data sharing the same cache line
+        // as buf.
+	dma_cache_wback_inv((unsigned long)buf,(unsigned long)nbytes);
+#endif
 	dp->dscr_cmd0 |= DSCR_CMD0_V;	/* Let it rip */
+#if defined(CONFIG_NONCOHERENT_IO)
 	au_sync();
-	dma_cache_wback_inv(dp, sizeof(dp));
+	dma_cache_wback_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
         ctp->chan_ptr->ddma_dbell = 0;
-	
+	au_sync();
+
 	/* Get next descriptor pointer.
 	*/
-	ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+	ctp->ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 
 	/* return something not zero.
 	*/
@@ -692,7 +757,10 @@
 	 * an interrupt doesn't affect this pointer nor the descriptor,
 	 * so no locking should be needed.
 	 */
-	dp = ctp->get_ptr;
+	dp = ctp->ptr;
+#if defined(CONFIG_NONCOHERENT_IO)
+	dma_cache_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
 
 	/* If the descriptor is valid, we are way ahead of the DMA
 	 * engine, so just return an error condition.
@@ -708,7 +776,7 @@
 	
 	/* Get next descriptor pointer.
 	*/
-	ctp->get_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+	ctp->ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 
 	/* return something not zero.
 	*/
@@ -753,7 +821,6 @@
 
 	ctp = *((chan_tab_t **)chanid);
 	cp = ctp->chan_ptr;
-	cp->ddma_desptr = virt_to_phys(ctp->cur_ptr);
 	cp->ddma_cfg |= DDMA_CFG_EN;	/* Enable channel */
 	au_sync();
 	cp->ddma_dbell = 0;
@@ -765,12 +832,16 @@
 au1xxx_dbdma_reset(u32 chanid)
 {
 	chan_tab_t		*ctp;
+	au1x_dma_chan_t		*cp;
 	au1x_ddma_desc_t	*dp;
 
 	au1xxx_dbdma_stop(chanid);
 
 	ctp = *((chan_tab_t **)chanid);
-	ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base;
+	cp = ctp->chan_ptr;
+	ctp->ptr = ctp->chan_desc_base;
+	cp->ddma_desptr = virt_to_phys(ctp->chan_desc_base);
+	au_sync();
 
 	/* Run through the descriptors and reset the valid indicator.
 	*/
@@ -778,13 +849,18 @@
 
 	do {
 		dp->dscr_cmd0 &= ~DSCR_CMD0_V;
+		/* want to save the D-cache!! */
 		/* reset our SW status -- this is used to determine 
 		 * if a descriptor is in use by upper level SW. Since 
 		 * posting can reset 'V' bit. 
 		 */
-		dp->sw_status = 0;
+#if defined(CONFIG_NONCOHERENT_IO)
+		au_sync();
+		dma_cache_wback_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
 		dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 	} while (dp != ctp->chan_desc_base);
+	au_sync();
 }
 EXPORT_SYMBOL(au1xxx_dbdma_reset);
 
@@ -819,7 +895,7 @@
 	au1xxx_dbdma_stop(chanid);
 
 	if (ctp->chan_desc_base != NULL)
-		kfree(ctp->chan_desc_base);
+		kfree((const void *)ctp->chan_desc_base);
 	
 	stp->dev_flags &= ~DEV_FLAGS_INUSE;
 	dtp->dev_flags &= ~DEV_FLAGS_INUSE;
@@ -832,19 +908,21 @@
 static void
 dbdma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	u32        				intstat, flags;
+	u32	intstat;
 	u32	chan_index;
 	chan_tab_t		*ctp;
-	au1x_ddma_desc_t	*dp;
 	au1x_dma_chan_t *cp;
 
+	if(!dbdma_initialized) {
+	    panic("dbdma: Got interrupt before initialized\n.");
+	}
+
 	intstat = dbdma_gptr->ddma_intstat;
 	au_sync();
 	chan_index = au_ffs(intstat) - 1;
 
 	ctp = chan_tab_ptr[chan_index];
 	cp = ctp->chan_ptr;
-	dp = ctp->cur_ptr;
 
 	/* Reset interrupt.
 	*/
@@ -854,7 +932,6 @@
 	if (ctp->chan_callback)
 		(ctp->chan_callback)(irq, ctp->chan_callparam, regs);
 
-	ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 }
 
 static void au1xxx_dbdma_init(void)
@@ -895,9 +972,8 @@
 
 	printk("Chan %x, stp %x (dev %d)  dtp %x (dev %d) \n",
 		(u32)ctp, (u32)stp, stp - dbdev_tab, (u32)dtp, dtp - dbdev_tab);
-	printk("desc base %x, get %x, put %x, cur %x\n",
-		(u32)(ctp->chan_desc_base), (u32)(ctp->get_ptr),
-		(u32)(ctp->put_ptr), (u32)(ctp->cur_ptr));
+	printk("desc base %x, ptr %x\n",
+	       (u32)(ctp->chan_desc_base), (u32)(ctp->ptr));
 	
 	printk("dbdma chan %x\n", (u32)cp);
 	printk("cfg %08x, desptr %08x, statptr %08x\n",
@@ -920,6 +996,7 @@
 		dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 	} while (dp != ctp->chan_desc_base);
 }
+EXPORT_SYMBOL(au1xxx_dbdma_dump);
 
 /* Put a descriptor into the DMA ring.
  * This updates the source/destination pointers and byte count.
@@ -940,7 +1017,10 @@
 	* an interrupt doesn't affect this pointer nor the descriptor,
 	* so no locking should be needed.
 	*/
-	dp = ctp->put_ptr;
+	dp = ctp->ptr;
+#if defined(CONFIG_NONCOHERENT_IO)
+	dma_cache_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
 
 	/* If the descriptor is valid, we are way ahead of the DMA
 	* engine, so just return an error condition.
@@ -959,16 +1039,76 @@
 	/* Allow the caller to specifiy if an interrupt is generated */
 	dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
 	dp->dscr_cmd0 |= dscr->dscr_cmd0 | DSCR_CMD0_V;
+#if defined(CONFIG_NONCOHERENT_IO)
+	au_sync();
+	dma_cache_wback_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
 	ctp->chan_ptr->ddma_dbell = 0;
+	au_sync();
 
 	/* Get next descriptor pointer.
 	*/
-	ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+	ctp->ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 
 	/* return something not zero.
 	*/
 	return nbytes;
 }
 
+/* Put a descriptor into the DMA ring.
+ * This updates the byte count only.  Normally during fifo to fifo
+ * transfers. 
+ */
+u32
+au1xxx_dbdma_put_dscr2(u32 chanid, int nbytes)
+{
+        chan_tab_t              *ctp;
+        au1x_ddma_desc_t        *dp;
+
+        /* I guess we could check this to be within the
+         * range of the table......
+         */
+        ctp = *((chan_tab_t **)chanid);
+
+        /* We should have multiple callers for a particular channel,
+         * an interrupt doesn't affect this pointer nor the descriptor,
+         * so no locking should be needed.
+         */
+        dp = ctp->ptr;
+#if defined(CONFIG_NONCOHERENT_IO)
+	dma_cache_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
+
+        /* If the descriptor is valid, we are way ahead of the DMA
+         * engine, so just return an error condition.
+         */
+        if (dp->dscr_cmd0 & DSCR_CMD0_V)
+                return 0;
+
+        // Set ddma_irq upon descriptor completion
+        dp->dscr_cmd0 |= DSCR_CMD0_IE;
+
+        // Update length
+        dp->dscr_cmd1 = nbytes;
+
+        dp->dscr_cmd0 |= DSCR_CMD0_V;        /* Let it rip */
+#if defined(CONFIG_NONCOHERENT_IO)
+	au_sync();
+        dma_cache_wback_inv((unsigned long)dp, (unsigned long)sizeof(au1x_ddma_desc_t));
+#endif
+
+        ctp->chan_ptr->ddma_dbell = 0;
+        au_sync();
+        
+        /* Get next descriptor pointer.
+         */
+        ctp->ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+
+        /* return something not zero.
+         */
+        return nbytes;
+}
+EXPORT_SYMBOL(au1xxx_dbdma_put_dscr2);
+
 #endif /* defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) */
 
diff -x asm -Naur linux-2.4.30-orig/arch/mips/au1000/common/reset.c linux-2.4.30/arch/mips/au1000/common/reset.c
--- linux-2.4.30-orig/arch/mips/au1000/common/reset.c	2005-05-12 17:57:05.000000000 +0200
+++ linux-2.4.30/arch/mips/au1000/common/reset.c	2006-12-11 18:18:37.000000000 +0100
@@ -37,6 +37,32 @@
 #include <asm/system.h>
 #include <asm/au1000.h>
 
+/* Paranoid reset of dbdma
+ */
+static void reset_dbdma(void)
+{
+    int i;
+
+    /* Reset each channel
+     */
+    for(i = 0; i < 16; i++) {
+	au_writel(0, 0xb4002000 + 0x100*i + 0x00); /* cfg */
+	au_writel(0, 0xb4002000 + 0x100*i + 0x04); /* desptr */
+	au_writel(0, 0xb4002000 + 0x100*i + 0x08); /* statptr */
+	au_writel(0, 0xb4002000 + 0x100*i + 0x0c); /* dbell */
+	au_writel(0, 0xb4002000 + 0x100*i + 0x10); /* interrupt request */
+	au_writel(1, 0xb4002000 + 0x100*i + 0x14); /* status */
+	au_writel(0, 0xb4002000 + 0x100*i + 0x18); /* remaining byte count */
+    }
+
+    /* Reset global.
+     */
+    au_writel(0, 0xb4003000 + 0x00); /* config */
+    au_writel(0, 0xb4003000 + 0x00); /* intstat */
+    au_writel(0, 0xb4003000 + 0x00); /* throttle */
+    au_writel(0, 0xb4003000 + 0x00); /* inten */
+}
+
 void au1000_restart(char *command)
 {
 	/* Set all integrated peripherals to disabled states */
@@ -120,6 +146,16 @@
 		au_writel(0x00, 0xb1900100); /* sys_pininputen */
 		break;
 	case 0x03000000: /* Au1550 */
+#ifdef CONFIG_MIPS_I3_MMB3
+		au_writel(0x00, MEM_STADDR3);
+		au_writel(0x00, MEM_STTIME3);
+		au_writel(0x00, MEM_STCFG3);
+		asm("sync");
+		/* This delay is unfortunately necessary
+		 * could possibly be solved by enabling IDE drive reset
+		 */
+		udelay(10);
+#endif
 		au_writel(0x00, 0xb1a00004); /* psc 0 */
 		au_writel(0x00, 0xb1b00004); /* psc 1 */
 		au_writel(0x00, 0xb0a00004); /* psc 2 */
@@ -141,6 +177,7 @@
 		au_writel(0x10, 0xb1900060); /* sys_cpupll */
 		au_writel(0x00, 0xb1900064); /* sys_auxpll */
 		au_writel(0x00, 0xb1900100); /* sys_pininputen */
+		reset_dbdma();
 		break;
 	case 0x04000000: /* Au1200 */
 		au_writel(0x00, 0xb400300c); /* ddma */
diff -x asm -Naur linux-2.4.30-orig/arch/mips/au1000/common/time.c linux-2.4.30/arch/mips/au1000/common/time.c
--- linux-2.4.30-orig/arch/mips/au1000/common/time.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/arch/mips/au1000/common/time.c	2006-12-11 18:18:37.000000000 +0100
@@ -560,5 +560,5 @@
 	/* Determine CP0 timer values */
 	printk("calculating r4koff... ");
 	r4k_offset = cal_r4koff();
-	printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
+	printk("%08x(%d)\n", r4k_offset, (int) r4k_offset);
 }
diff -x asm -Naur linux-2.4.30-orig/arch/mips/au1000/db1x00/board_setup.c linux-2.4.30/arch/mips/au1000/db1x00/board_setup.c
--- linux-2.4.30-orig/arch/mips/au1000/db1x00/board_setup.c	2005-05-12 17:57:05.000000000 +0200
+++ linux-2.4.30/arch/mips/au1000/db1x00/board_setup.c	2006-12-11 18:18:37.000000000 +0100
@@ -46,6 +46,7 @@
 #include <asm/au1000.h>
 #include <asm/db1x00.h>
 
+#if 0
 #if defined(CONFIG_BLK_DEV_IDE_AU1XXX) && defined(CONFIG_MIPS_DB1550)
 #include <asm/au1xxx_dbdma.h>
 extern struct ide_ops *ide_ops;
@@ -60,13 +61,16 @@
 
 dbdev_tab_t new_dbdev_tab_element = { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 };
 #endif /* end CONFIG_BLK_DEV_IDE_AU1XXX */
+#endif
 
 extern struct rtc_ops no_rtc_ops;
 
 void board_reset (void)
 {
+#ifndef CONFIG_MIPS_I3_MMB3
 	/* Hit BCSR.SYSTEM_CONTROL[SW_RST] */
 	au_writel(0x00000000, 0xAE00001C);
+#endif
 }
 
 void board_power_off (void)
@@ -78,7 +82,7 @@
 
 void __init board_setup(void)
 {
-	u32 pin_func;
+  //	u32 pin_func;
 
 	rtc_ops = &no_rtc_ops;
 
@@ -98,7 +102,10 @@
 	bcsr->resets |= BCSR_RESETS_IRDA_MODE_OFF;
 	au_sync();
 #endif
+
+#ifndef CONFIG_MIPS_I3_MMB3
 	au_writel(0, 0xAE000010); /* turn off pcmcia power */
+#endif
 
 #ifdef CONFIG_MIPS_MIRAGE
 	/* enable GPIO[31:0] inputs */
@@ -126,41 +133,73 @@
 	au_writel(au_readl(GPIO2_DIR) | 0x200, GPIO2_DIR);
 	au_writel(0x02000200, GPIO2_OUTPUT);
 #endif
-
-#if defined(CONFIG_AU1XXX_SMC91111)
-#define CPLD_CONTROL (0xAF00000C)
 	{
-	extern uint32_t au1xxx_smc91111_base;
-	extern unsigned int au1xxx_smc91111_irq;
-	extern int au1xxx_smc91111_nowait;
-
-	au1xxx_smc91111_base = 0xAC000300;
-	au1xxx_smc91111_irq = AU1000_GPIO_8;
-	au1xxx_smc91111_nowait = 1;
-
-	/* set up the Static Bus timing - only 396Mhz */
-	bcsr->resets |= 0x7;
-	au_writel(0x00010003, MEM_STCFG0);
-	au_writel(0x000c00c0, MEM_STCFG2);
-	au_writel(0x85E1900D, MEM_STTIME2);
+		int config;
+		// S:0(async), BEB=2 (recommended), H:0 (32bit bus),
+#define ST3_IDE_NW	0
+#define ST3_IDE_AS	0
+#define ST3_IDE_S	0
+#define ST3_IDE_DE	0
+#define ST3_IDE_BEB	2 // not change it. working
+#define ST3_IDE_BE	0
+#define ST3_IDE_TS	0
+#define ST3_IDE_EW	0
+#define ST3_IDE_H	0 // 0 or 1 either works with 16-16, 8,8 datums.
+	    // 1 not work with 16-32, 8-4 datums
+#define ST3_IDE_BS	0 //ONLY for NAND mod 8 beats
+#define ST3_IDE_PM	0 //ONLY for NAND mod
+#define ST3_IDE_RO	0
+#define ST3_IDE_DTY	2 // not change it
+
+		// Codekti Au1550 IDE over the Static RAM
+		//	    AS=1, DE=1  BEB=2, H=1
+		config = ((ST3_IDE_NW << 22) | (ST3_IDE_AS << 21) | (ST3_IDE_S << 20) |   
+			  (ST3_IDE_DE << 19) | (ST3_IDE_BEB << 17) | (ST3_IDE_BE << 9) |
+			  (ST3_IDE_TS << 8) | (ST3_IDE_EW << 7) | (ST3_IDE_H << 6) |
+			  (ST3_IDE_BS << 5) | (ST3_IDE_PM << 4) | (ST3_IDE_RO << 3) |
+			  (ST3_IDE_DTY << 0));
+		au_writel(config, MEM_STCFG3); // = 0x00040002
 	}
-#endif /* end CONFIG_SMC91111 */
+	{
+		int timing;
+
+#if 0 // AMD_PATCH
+#define ST3_IDE_Tmst	0
+#define ST3_IDE_Tmsu	0
+#define ST3_IDE_Tmih	0
+#define ST3_IDE_Tist	7 // was 20
+#define ST3_IDE_Tisu	1
+#else
+// within the spec
+#define ST3_IDE_Tmst	0
+#define ST3_IDE_Tmsu	0
+#define ST3_IDE_Tmih	1
+#define ST3_IDE_Tist	10
+#define ST3_IDE_Tisu	3
+#endif
+		timing = ((ST3_IDE_Tmst << 24) | (ST3_IDE_Tmsu << 17) | (ST3_IDE_Tmih << 11) |
+			  (ST3_IDE_Tist << 5) | (ST3_IDE_Tisu << 0));
+        	au_writel(timing, MEM_STTIME3); // PIO 4, 16.67 MB/s, was 0x00000985
+        	//0x00000943
+        }
+
+	au_writel(0x10003fff, MEM_STADDR3); // Set mask
 	au_sync();
 
-#if defined(CONFIG_BLK_DEV_IDE_AU1XXX) && defined(CONFIG_MIPS_DB1550)
-	/*
-	 * Iniz IDE parameters
-	 */
-	ide_ops = &au1xxx_ide_ops;
-	au1xxx_ide_irq = DAUGHTER_CARD_IRQ;
-	au1xxx_ide_physbase = AU1XXX_ATA_PHYS_ADDR;
-	au1xxx_ide_virtbase = KSEG1ADDR(AU1XXX_ATA_PHYS_ADDR);
-
-	/*
-	 * change PIO or PIO+Ddma
-	 * check the GPIO-6 pin condition. db1550:s6_dot
-	 */
-	switch4ddma = (au_readl(SYS_PINSTATERD) & (1 << 6)) ? 1 : 0;
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX)
+ 	extern struct ide_ops *ide_ops;
+ 	extern struct ide_ops auide_ops;
+ 	extern void ide_register_driver(ide_driver_call);
+ 	extern int auide_probe (void);
+
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+#if defined(CONFIG_PCMCIA)
+#error "Please turn off CONFIG_PCMCIA"
+#endif
+//	pcmcia_setup_for_ide();
+#endif
+ 	ide_ops = &auide_ops;
+ 	ide_register_driver( auide_probe );
 #endif
 
 #ifdef CONFIG_MIPS_DB1000
@@ -181,4 +220,7 @@
 #ifdef CONFIG_MIPS_DB1550
     printk("AMD Alchemy Au1550/Db1550 Board\n");
 #endif
+#ifdef CONFIG_MIPS_I3_MMB3
+    printk("I3 Micro Technology Mood 300 STB\n");
+#endif
 }
diff -x asm -Naur linux-2.4.30-orig/arch/mips/au1000/db1x00/init.c linux-2.4.30/arch/mips/au1000/db1x00/init.c
--- linux-2.4.30-orig/arch/mips/au1000/db1x00/init.c	2005-05-12 17:57:05.000000000 +0200
+++ linux-2.4.30/arch/mips/au1000/db1x00/init.c	2006-12-11 18:18:37.000000000 +0100
@@ -48,6 +48,8 @@
 {
 #ifdef CONFIG_MIPS_BOSPORUS
 	return "Alchemy Bosporus Gateway Reference";
+#elif defined CONFIG_MIPS_I3_MMB3
+	return "i3 Mood Series 300";
 #else
 	return "Alchemy Db1x00";
 #endif
diff -x asm -Naur linux-2.4.30-orig/arch/mips/au1000/db1x00/irqmap.c linux-2.4.30/arch/mips/au1000/db1x00/irqmap.c
--- linux-2.4.30-orig/arch/mips/au1000/db1x00/irqmap.c	2005-05-12 17:57:05.000000000 +0200
+++ linux-2.4.30/arch/mips/au1000/db1x00/irqmap.c	2006-12-11 18:18:37.000000000 +0100
@@ -50,9 +50,10 @@
 au1xxx_irq_map_t au1xxx_irq_map[] = {
 
 #ifndef CONFIG_MIPS_MIRAGE
-#ifdef CONFIG_MIPS_DB1550
+#if defined(CONFIG_MIPS_DB1550) || defined(CONFIG_MIPS_I3_MMB3)
 	{ AU1000_GPIO_3, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 0 IRQ#
 	{ AU1000_GPIO_5, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 1 IRQ#
+	{ AU1500_GPIO_207, INTC_INT_HIGH_LEVEL, 0 },
 	{ AU1000_GPIO_8, INTC_INT_LOW_LEVEL, 0 }, // Daughtercard IRQ#
 #else
 	{ AU1000_GPIO_0, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 0 Fully_Interted#
@@ -79,7 +80,7 @@
 #define INTD AU1000_PCI_INTD
 #endif
 
-#ifdef CONFIG_SOC_AU1550
+#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_MIPS_I3_MMB3)
 #define INTA AU1550_PCI_INTA
 #define INTB AU1550_PCI_INTB
 #define INTC AU1550_PCI_INTC
@@ -133,6 +134,23 @@
 	};
 	const long min_idsel = 11, max_idsel = 13, irqs_per_slot = 4;
 #endif
+
+#ifdef CONFIG_MIPS_I3_MMB3
+	static char pci_irq_table[][4] =
+	{
+		{INTA, INTX, INTX, INTX},   /* IDSEL  5 - on-board TI 642    */
+		{INTX, INTX, INTX, INTX},   /* IDSEL  6 - unused             */
+		{INTX, INTX, INTX, INTX},   /* IDSEL  7 - unused             */
+		{INTX, INTX, INTX, INTX},   /* IDSEL  8 - unused             */
+		{INTX, INTX, INTX, INTX},   /* IDSEL  9 - unused             */
+		{INTX, INTX, INTX, INTX},   /* IDSEL 10 - unused             */
+		{INTC, INTX, INTX, INTX},   /* IDSEL 11 - on-board HPT371    */
+		{INTB, INTC, INTD, INTA},   /* IDSEL 12 - PCI slot 2 (left)  */
+		{INTA, INTB, INTC, INTD},   /* IDSEL 13 - PCI slot 1 (right) */
+	};
+	const long min_idsel = 5, max_idsel = 13, irqs_per_slot = 4;
+#endif
+
 #if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1500)
 	return PCI_IRQ_TABLE_LOOKUP;
 #else
diff -x asm -Naur linux-2.4.30-orig/arch/mips/config-shared.in linux-2.4.30/arch/mips/config-shared.in
--- linux-2.4.30-orig/arch/mips/config-shared.in	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/arch/mips/config-shared.in	2006-12-11 18:18:37.000000000 +0100
@@ -27,6 +27,7 @@
 dep_bool 'Support for Alchemy Db1100 board' CONFIG_MIPS_DB1100 $CONFIG_MIPS32
 dep_bool 'Support for Alchemy Db1500 board' CONFIG_MIPS_DB1500 $CONFIG_MIPS32
 dep_bool 'Support for Alchemy Db1550 board' CONFIG_MIPS_DB1550 $CONFIG_MIPS32
+dep_bool 'Support for I3 Micro MMB3 board'  CONFIG_MIPS_I3_MMB3 $CONFIG_MIPS32
 dep_bool 'Support for Alchemy Db1200 board' CONFIG_MIPS_DB1200 $CONFIG_MIPS32
 dep_bool 'Support for Alchemy PB1000 board' CONFIG_MIPS_PB1000 $CONFIG_MIPS32
 dep_bool 'Support for Alchemy PB1100 board' CONFIG_MIPS_PB1100 $CONFIG_MIPS32
@@ -334,6 +335,13 @@
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
 fi
+if [ "$CONFIG_MIPS_I3_MMB3" = "y" ]; then
+   define_bool CONFIG_SOC_AU1X00 y
+   define_bool CONFIG_SOC_AU1550 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ n
+   define_bool CONFIG_NONCOHERENT_IO y
+   define_bool CONFIG_PC_KEYB y
+fi
 if [ "$CONFIG_MIPS_DB1200" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
    define_bool CONFIG_SOC_AU1200 y
@@ -770,6 +778,7 @@
      "$CONFIG_MIPS_DB1500" = "y" -o \
      "$CONFIG_MIPS_DB1550" = "y" -o \
      "$CONFIG_MIPS_DB1200" = "y" -o \
+     "$CONFIG_MIPS_I3_MMB3" = "y" -o \
      "$CONFIG_NEC_OSPREY" = "y" -o \
      "$CONFIG_NEC_EAGLE" = "y" -o \
      "$CONFIG_NINO" = "y" -o \
diff -x asm -Naur linux-2.4.30-orig/arch/mips/ramdisk/Makefile linux-2.4.30/arch/mips/ramdisk/Makefile
--- linux-2.4.30-orig/arch/mips/ramdisk/Makefile	2005-05-12 17:57:08.000000000 +0200
+++ linux-2.4.30/arch/mips/ramdisk/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -10,6 +10,6 @@
 img = $(CONFIG_EMBEDDED_RAMDISK_IMAGE)
 ramdisk.o: $(subst ",,$(img)) ld.script
 	echo "O_FORMAT:  " $(O_FORMAT)
-	$(LD) $(LDFLAGS) -T ld.script -b binary --oformat $(O_FORMAT) -o $@ $(img)
+	$(LD) -T ld.script -b binary --oformat $(O_FORMAT) -o $@ $(img)
 
 include $(TOPDIR)/Rules.make
diff -x asm -Naur linux-2.4.30-orig/drivers/char/Config.in linux-2.4.30/drivers/char/Config.in
--- linux-2.4.30-orig/drivers/char/Config.in	2005-05-12 17:57:19.000000000 +0200
+++ linux-2.4.30/drivers/char/Config.in	2006-12-11 18:18:37.000000000 +0100
@@ -393,7 +393,7 @@
 if [ "$CONFIG_SOC_AU1X00" = "y" ]; then
    tristate ' Alchemy Au1x00 GPIO device support' CONFIG_AU1X00_GPIO
    tristate ' Au1000/ADS7846 touchscreen support' CONFIG_TS_AU1X00_ADS7846
-   #tristate ' Alchemy Au1550 PSC SPI support' CONFIG_AU1550_PSC_SPI
+   tristate ' Alchemy Au1550 PSC SPI support' CONFIG_AU1550_PSC_SPI
 fi
 if [ "$CONFIG_MIPS_ITE8172" = "y" ]; then
   tristate ' ITE GPIO' CONFIG_ITE_GPIO
diff -x asm -Naur linux-2.4.30-orig/drivers/char/au1550_psc_spi.c linux-2.4.30/drivers/char/au1550_psc_spi.c
--- linux-2.4.30-orig/drivers/char/au1550_psc_spi.c	2005-05-12 17:57:19.000000000 +0200
+++ linux-2.4.30/drivers/char/au1550_psc_spi.c	2006-12-11 18:18:37.000000000 +0100
@@ -49,6 +49,10 @@
 #include <asm/pb1200.h>
 #endif
 
+#ifdef CONFIG_MIPS_I3_MMB3
+#include <asm/mood300.h>
+#endif
+
 /* This is just a simple programmed I/O SPI interface on the PSC of the 1550.
  * We support open, close, write, and ioctl.  The SPI is a full duplex
  * interface, you can't read without writing.  So, the write system call
@@ -67,6 +71,10 @@
 */
 #define MAX_BAUD_RATE	(2 * 1000000)
 #define PSC_INTCLK_RATE (32 * 1000000)
+#define DEFAULT_BAUD_RATE (1*1000000)
+
+// Loopback mode default on
+//#define LOOPBACK 
 
 static	int	inuse;
 
@@ -75,12 +83,13 @@
  * and we have to load it with the bits to go in a single transfer.
  */
 static	uint	spi_datalen;
+static  uint    spi_peer = AU1550SPI_PEER_AVR;
 
 static int
 au1550spi_master_done( int ms )
 {
-	int timeout=ms;
 	volatile psc_spi_t *sp;
+	int timeout=ms;
 
 	sp = (volatile psc_spi_t *)SPI_PSC_BASE;
 
@@ -98,6 +107,8 @@
 static int
 au1550spi_open(struct inode *inode, struct file *file)
 {
+        volatile psc_spi_t *sp = (volatile psc_spi_t *)SPI_PSC_BASE;
+  
 	if (inuse)
 		return -EBUSY;
 
@@ -105,18 +116,58 @@
 
 	MOD_INC_USE_COUNT;
 
+#if LOOPBACK
+        // On
+        sp->psc_spicfg |= ~PSC_SPICFG_LB;
+#else
+        // Off
+        sp->psc_spicfg &= ~PSC_SPICFG_LB;
+#endif
+        au_sync();
+        
 	return 0;
 }
 
+static void
+assert_peer(void)
+{
+    volatile u32 * gpio_outputset = (u32 *)0xb1700108lu;
+    u32 val;
+    
+    val = (spi_peer << 16);
+
+//    printk("spi-assert: gpio2 before = 0x%p\n", (u32 *)*gpio_outputset);
+    *gpio_outputset = val;
+//    printk("spi-assert: gpio2 after = 0x%p\n", (u32 *)*gpio_outputset);
+}
+
+static void
+deassert_peer(void)
+{
+    volatile u32 * gpio_outputset = (u32 *)0xb1700108lu;
+    u32 val;
+    
+    val = (spi_peer << 16) | spi_peer;
+
+//    printk("spi-deassert: gpio2 before = 0x%p\n", (u32 *)*gpio_outputset);
+    *gpio_outputset = val;
+//    printk("spi-deassert: gpio2 after = 0x%p\n", (u32 *)*gpio_outputset);
+}
+
 static ssize_t
 au1550spi_write(struct file *fp, const char *bp, size_t count, loff_t *ppos)
 {
 	int	bytelen, i;
 	size_t	rcount, retval;
 	unsigned char	sb, *rp, *wp;
-	uint	fifoword, pcr, stat;
+	uint	fifoword, pcr, stat, ev;
 	volatile psc_spi_t *sp;
 
+        int ntries;
+#define MAX_TRIES 1000000*10
+
+//	printk("au1550spi_write(0x%p, 0x%p, %d, 0x%p)\n", fp, bp, count, ppos);
+
 	/* Get the number of bytes per transfer.
 	*/
 	bytelen = ((spi_datalen - 1) / 8) + 1;
@@ -126,6 +177,8 @@
 	if ((count % bytelen) != 0)
 		return -EINVAL;
 
+	assert_peer();
+
 	rp = wp = (unsigned char *)bp;
 	retval = rcount = count;
 
@@ -139,6 +192,9 @@
 		au_sync();
 	} while (pcr != 0);
 
+        // Must delay here so that the fifo reset above has time to finish
+        udelay(1);
+
 	/* Prime the transmit FIFO.
 	*/
 	while (count > 0) {
@@ -206,10 +262,21 @@
 	 * out waiting for any residual bytes that are yet to be
 	 * read from the fifo.
 	 */
+        ntries = 0;
 	while (rcount > 0) {
+
+                if (ntries++ > MAX_TRIES) {
+                        ev = sp->psc_spievent;
+                        stat = sp->psc_spistat;
+                        au_sync();
+                        printk("[E %d rcount=%d ev=0x%x stat=0x%x]\n", __LINE__, rcount, ev, stat);
+                        break;
+                }
+
 		stat = sp->psc_spistat;
 		au_sync();
 		if ((stat & PSC_SPISTAT_RE) == 0) {
+                        ntries = 0;
 			fifoword = sp->psc_spitxrx;
 			au_sync();
 			for (i=0; i<bytelen; i++) {
@@ -223,6 +290,8 @@
 		}
 	}
 
+	deassert_peer();
+
 	/* Wait for MasterDone event. 30ms timeout */
 	if (!au1550spi_master_done(30) ) retval = -EFAULT;
 	return retval;
@@ -248,6 +317,7 @@
 	volatile psc_spi_t *sp;
 
 	/* For starters, the input clock is divided by two.
+           (done in au1550spi_init(), DIV bits in psc_spicfg)
 	*/
 	tmpclk = PSC_INTCLK_RATE/2;
 
@@ -260,6 +330,12 @@
 
 	 /* Test BRG to ensure it will fit into the 6 bits allocated.
 	 */
+	 if((brg > 63) || (brg < 4)) {
+	     printk("spi: ERROR!!!! set_baud_rate:  %d baud invalid (brg=%d)!\n", baud, brg);
+	     return -EINVAL;
+	 } else {
+                 //printk("spi: set_baud_rate - (baude=%d,brg=%d)!\n", baud, brg);
+         }
 
 	 /* Make sure the device is disabled while we make the change.
 	 */
@@ -325,6 +401,21 @@
 }
 
 static int
+set_peer(unsigned long peer)
+{
+    switch(peer) {
+    case AU1550SPI_PEER_NET:
+    case AU1550SPI_PEER_AVR:
+	spi_peer = peer;
+	break;
+    default:
+	return -1;
+    }
+
+    return 0;
+}
+
+static int
 au1550spi_ioctl(struct inode *inode, struct file *file,
 			    unsigned int cmd, unsigned long arg)
 {
@@ -343,10 +434,30 @@
 			return -EFAULT;
 
 		val = set_baud_rate(val);
+                if (val == -EINVAL)
+                        return -EINVAL;
 		if (put_user(val, (u32 *)arg)) 
 			return -EFAULT;
 		break;
 
+	case AU1550SPI_SET_PEER:
+	    if(set_peer(arg) != 0)
+		return -EFAULT;
+	    break;
+
+        case AU1550SPI_LOOPBACK: {
+                volatile psc_spi_t *sp = (volatile psc_spi_t *)SPI_PSC_BASE;
+		if (arg != 0)
+                        // On
+                        sp->psc_spicfg |= PSC_SPICFG_LB;
+                else
+                        // Off
+                        sp->psc_spicfg &= ~PSC_SPICFG_LB;
+                au_sync();
+                printk("spi: Loopback %s\n", arg != 0 ? "on" : "off");
+                break;
+        }
+
 	default:
 		status = -ENOIOCTLCMD;
 
@@ -410,6 +521,7 @@
 	au_writel(clk, SYS_CLKSRC);
 	au_sync();
 
+#ifndef CONFIG_MIPS_I3_MMB3
 	/* Set up GPIO pin function to drive PSC0_SYNC1, which is
 	 * the SPI Select.
 	 */
@@ -418,6 +530,7 @@
 	clk |= 1;
 	au_writel(clk, SYS_PINFUNC);
 	au_sync();
+#endif
 
 	/* Now, set up the PSC for SPI PIO mode.
 	*/
@@ -437,11 +550,18 @@
 	sp->psc_spicfg = (PSC_SPICFG_RT_FIFO8 | PSC_SPICFG_TT_FIFO8 |
 				PSC_SPICFG_DD_DISABLE | PSC_SPICFG_MO);
 	sp->psc_spicfg |= PSC_SPICFG_SET_LEN(8);
+
+	sp->psc_spicfg |= PSC_SPICFG_CDE;
+
+        // Set tmpclk to .5 * main clock (main clock =  FREQ3).
+        // This rate is assumed in set_baud_rate()!
+        sp->psc_spicfg |= PSC_SPICFG_SET_DIV(PSC_SPICFG_DIV2);
+
 	spi_datalen = 8;
 	sp->psc_spimsk = PSC_SPIMSK_ALLMASK;
 	au_sync();
 
-	set_baud_rate(1000000);
+	set_baud_rate(DEFAULT_BAUD_RATE);
 
 	sp->psc_spicfg |= PSC_SPICFG_DE_ENABLE;
 	do {
diff -x asm -Naur linux-2.4.30-orig/drivers/char/rtc.c linux-2.4.30/drivers/char/rtc.c
--- linux-2.4.30-orig/drivers/char/rtc.c	2005-05-12 17:57:21.000000000 +0200
+++ linux-2.4.30/drivers/char/rtc.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,4 +1,74 @@
 /*
+ * A Real Time Clock interface for AMD Alchemy Au1550 based on the RTC & TOY clock.
+ * Note: time will be lost, when power is switched off.
+ *
+ * Copyright (C) 2005 I3 Micro Technology AB
+ *
+ *  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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE	LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  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.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ * Author: Karl Monell
+ *
+ * The TOY-registers counter and match[0:2] are 32-bit registers.
+ * The external clock source applied at GPIO[1] is 1 kHz. The
+ * pre-counter divide is set to divide by 8 in this module, giving 125 ticks
+ * per second. Thus the counter will wrap around after 2��/125 s (roughly 
+ * 398 days). The idea is to use match[0] to detect wrap-around, match[1] for 
+ * an alarm and match[2] to store the year (thus year will get the same reset
+ * properties as counter). A tick interrupt may come from the counter itself.
+ * The RTC-registers counter and match[0:2] are 32-bit registers.
+ * The external clock source applied at GPIO[1] is 1 kHz. Initially the
+ * pre-counter divide is set to divide by 8 in this module, giving 125 ticks
+ * per second. Thus the counter will wrap around after 2��/125 s (roughly 
+ * 398 days). The counter stops counting during sleep and hibernate.
+ * The idea is to use match[0]. A periodical interrupt may come from the counter
+ * itself.
+ * register        irq    use
+ * --------        ---    ---
+ * toy counter      14    tick interrupt
+ * toy match[0]     15    wrap around
+ * toy match[1]     16    alarm n. y. i.
+ * toy match[2]     17    year storage - never enabled
+ * rtc counter      18    periodical interrupt
+ * rtc match[0]     19    wrap around
+ * rtc match[1]     20    spare
+ * rtc match[2]     21    spare
+ * DST is ignored. Roadmap: 1) implement alarm.
+ *
+ *
+ * Version history:
+ * v0.1	- [kam] Initial version
+ * v0.2	- [kam] Periodic interrupt added with fixed f=125 Hz.
+ * v0.3	- [kam] Removed a reminiscence of an alternative solution.
+ * v0.4 - [kam] Using both "braces" and "belt" to wake up.
+ * v0.5	- [kam] RTC_IRQP_READ implemented. The answer is the integer part
+ *              of the periodic frequency.
+ * v0.6	- [kam] RTC_IRQP_SET implemented. Acceptable frequency values are 
+ *              1000/n (n is an integer) rounded to an integer > 0. The
+ *              value maybe rounded upwards or downwards. The command 
+ *              argument is an integer but the resulting frequency will
+ *              be a real value (as reported by /proc/driver/rtc). If the
+ *              kernel refuse the use of irq 19, the driver will fall back
+ *              to using irq 14 and a fixed frequency of 125 Hz.
+ *
+ * Replaces:
+ *
  *	Real Time Clock interface for Linux	
  *
  *	Copyright (C) 1996 Paul Gortmaker
@@ -44,1125 +114,1145 @@
  *	1.10f	Maciej W. Rozycki: Handle memory-mapped chips properly.
  */
 
-#define RTC_VERSION		"1.10f"
-
-/*
- *	Note that *all* calls to CMOS_READ and CMOS_WRITE are done with
- *	interrupts disabled. Due to the index-port/data-port (0x70/0x71)
- *	design of the RTC, we don't want two different things trying to
- *	get to it at once. (e.g. the periodic 11 min sync from time.c vs.
- *	this driver.)
- */
+#define DRIVER "AMD Alchemy Au1550 RTC Driver "
+#define RTC_VERSION "v0.6"
 
 #include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/ioport.h>
-#include <linux/fcntl.h>
-#include <linux/mc146818rtc.h>
-#include <linux/init.h>
-#include <linux/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/spinlock.h>
-#include <linux/sysctl.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
+#include <linux/module.h>     /* THIS_MODULE MODULE_* EXPORT_NO_SYMBOLS config.h spinlock.h list.h */
+#include <linux/kernel.h>     /* KERN_WARNING etc */
+#include <linux/miscdevice.h> /* RTC_MINOR miscdevice misc_*register */
+                              /* devfs_fs_kernel.h: -> fs.h  config.h spinlock.h */
+                              /* kdev_t.h types.h: (s)size_t loff_t*/
+                              /* fs.h: file_operations no_llseek -> config.h */
+                              /* limits.h wait.h: wait_queue_head_t DECLARE_WAIT_QUEUE_HEAD */
+                              /* types.h vfs.h net.h kdev_t.h */
+                              /* ioctl.h list.h dcache.h stat.h cache.h stddef.h */
+                              /* linkage.h string.h atomic.h bitops.h */
+#include <linux/init.h>       /* __init __exit module_init module_exit config.h compiler.h */
+#include <linux/proc_fs.h>    /* remove_proc_entry create_proc_read_entry */
+#include <linux/spinlock.h>   /* spinlock_t SPIN_LOCK_UNLOCKED spin_lock_irqsave */
+                              /* config.h system.h */
+#include <asm/io.h>           /* readl, writel */
+#include <asm/uaccess.h>      /* copy_*_user put_user */
 #include <asm/system.h>
+#include <asm/errno.h>        /* EACCES etc */
+#include <asm/au1000.h>       /* SYS & IC register addresses, SYS_CNTRL masks */
+#include <linux/sched.h>      /* request_irq capable */
+                              /* config.h binfmts.h threads.h kernel.h types.h */
+                              /* times.h timex.h rbtree.h system.h semaphore.h */
+                              /* page.h ptrace.h mmu.h smp.h tty.h sem.h */
+                              /* signal.h securebits.h fs_struct.h */
+#include <linux/capability.h> /* CAP_SYS_TIME */
+
+/* Check configuration */
+#if !defined(CONFIG_MIPS) || !defined(CONFIG_RTC)
+#error "This driver is for AMD Alchemy Au1550 using TOY clock as RTC."
+#endif
+
+#include <asm/time.h> /* ptrace.h: pt_regs, linkage.h rtc.h rtc_time RTC_*_TIME rtc_*_time */
+
+//#define RTC_DEBUG 1
+
+#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
+#define RTCTICK_PTR NULL
+#define TOYTICK_PTR NULL
+#define RTCMATCH0_PTR NULL
+#define TOYMATCH0_PTR NULL
+
+const unsigned int epoch = 1900;
+const unsigned long trim_default = 7; 
+const unsigned long freq = 1000; /* external clock frequency in Hz */
+const unsigned long timeout = 100000; /* at least one tick */
+/* ticks 1900: 365 * 24 * 60 * 60 * freq / (trim_default + 1) */
+const unsigned long yearly_ticks_default = 0xEAF62580; /* 3942000000 */
+const unsigned long yearly_seconds_normal = 31536000;
+const unsigned long addl_seconds_leap     = 86400;
+
+static unsigned long rtc_status = 0;	
+static unsigned long year_irq = 0;	
+static unsigned long eon_irq = 0;	
+static unsigned long spurious_irq = 0;	
+static unsigned long write_error = 0;	
+#ifdef RTC_DEBUG
+static unsigned long cp0_status = 0xFFFFFFFF;
+static unsigned long cp0_cause = 0xFFFFFFFF;
+//static unsigned long ic0_risingrd = 0xFFFFFFFF;
+//static unsigned long ic0_fallingrd = 0xFFFFFFFF;
+//static unsigned long ic0_req0int = 0xFFFFFFFF;
+static unsigned short sometimes = 0;
+static unsigned long tick_stat [256];
+static int sleep_diff = -1234;       /* a negative value far from 0 */
+#endif
+
+/* rtc_read variables section */
+static unsigned char got_period_irq = 0;
+static unsigned char got_tick_irq = 0;
+//static wait_queue_head_t period_q; 
+DECLARE_WAIT_QUEUE_HEAD (period_q);  /* periodic queue */
+static unsigned long new_year = 0;   /* rtc_read needs to remember */
+static unsigned long old_year = 0;   /* rtc_read needs to remember */
+static unsigned long new_cnt = 0;    /* rtc_read needs to remember */
+static unsigned long old_cnt = 0;    /* rtc_read needs to remember */
+static unsigned long new_eon = 0;    /* rtc_read needs to remember */
+static unsigned long old_eon = 0;    /* rtc_read needs to remember */
+static unsigned long new_pcnt = 0;   /* rtc_read needs to remember */
+static unsigned long old_pcnt = 0;   /* rtc_read needs to remember */
+static unsigned char stab = 0;       /* stability, 0 = uninitialized */
+
+static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
+
+static spinlock_t rtc_init_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t rtc_open_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t rtc_set_lock = SPIN_LOCK_UNLOCKED;
 
-#ifdef __sparc__
-#include <linux/pci.h>
-#include <asm/ebus.h>
-#ifdef __sparc_v9__
-#include <asm/isa.h>
-#endif
-
-static unsigned long rtc_port;
-static int rtc_irq = PCI_IRQ_NONE;
-#endif
-
-#if RTC_IRQ
-static int rtc_has_irq = 1;
-#endif
-
-/*
- *	We sponge a minor off of the misc major. No need slurping
- *	up another valuable major dev number for this. If you add
- *	an ioctl, make sure you don't conflict with SPARC's RTC
- *	ioctls.
- */
-
-static struct fasync_struct *rtc_async_queue;
-
-static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
-
-#if RTC_IRQ
-static struct timer_list rtc_irq_timer;
-#endif
-
-static ssize_t rtc_read(struct file *file, char *buf,
-			size_t count, loff_t *ppos);
-
-static int rtc_ioctl(struct inode *inode, struct file *file,
-		     unsigned int cmd, unsigned long arg);
-
-#if RTC_IRQ
-static unsigned int rtc_poll(struct file *file, poll_table *wait);
-#endif
-
-static void get_rtc_time (struct rtc_time *rtc_tm);
-static void get_rtc_alm_time (struct rtc_time *alm_tm);
-#if RTC_IRQ
-static void rtc_dropped_irq(unsigned long data);
-
-static void set_rtc_irq_bit(unsigned char bit);
-static void mask_rtc_irq_bit(unsigned char bit);
-#endif
-
-static inline unsigned char rtc_is_updating(void);
-
-static int rtc_read_proc(char *page, char **start, off_t off,
-                         int count, int *eof, void *data);
-
-/*
- *	Bits in rtc_status. (6 bits of room for future expansion)
- */
-
-#define RTC_IS_OPEN		0x01	/* means /dev/rtc is in use	*/
-#define RTC_TIMER_ON		0x02	/* missed irq timer active	*/
-
-/*
- * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is
- * protected by the big kernel lock. However, ioctl can still disable the timer
- * in rtc_status and then with del_timer after the interrupt has read
- * rtc_status but before mod_timer is called, which would then reenable the
- * timer (but you would need to have an awful timing before you'd trip on it)
- */
-static unsigned long rtc_status = 0;	/* bitmapped status byte.	*/
-static unsigned long rtc_freq = 0;	/* Current periodic IRQ rate	*/
-static unsigned long rtc_irq_data = 0;	/* our output to the world	*/
-static unsigned long rtc_max_user_freq = 64; /* > this, need CAP_SYS_RESOURCE */
-
-/*
- *	If this driver ever becomes modularised, it will be really nice
- *	to make the epoch retain its value across module reload...
- */
-
-static unsigned long epoch = 1900;	/* year corresponding to 0x00	*/
-
-static const unsigned char days_in_mo[] = 
-{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-#if RTC_IRQ
-/*
- *	A very tiny interrupt handler. It runs with SA_INTERRUPT set,
- *	but there is possibility of conflicting with the set_rtc_mmss()
- *	call (the rtc irq and the timer irq can easily run at the same
- *	time in two different CPUs). So we need to serializes
- *	accesses to the chip with the rtc_lock spinlock that each
- *	architecture should implement in the timer code.
- *	(See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
- */
-
-static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	/*
-	 *	Can be an alarm interrupt, update complete interrupt,
-	 *	or a periodic interrupt. We store the status in the
-	 *	low byte and the number of interrupts received since
-	 *	the last read in the remainder of rtc_irq_data.
-	 */
-
-	spin_lock (&rtc_lock);
-	rtc_irq_data += 0x100;
-	rtc_irq_data &= ~0xff;
-	rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
-
-	if (rtc_status & RTC_TIMER_ON)
-		mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
-
-	spin_unlock (&rtc_lock);
-
-	/* Now do the rest of the actions */
-	wake_up_interruptible(&rtc_wait);	
-
-	kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
-}
-#endif
-
-/*
- * sysctl-tuning infrastructure.
- */
-static ctl_table rtc_table[] = {
-    { 1, "max-user-freq", &rtc_max_user_freq, sizeof(int), 0644, NULL,
-      &proc_dointvec, NULL, },
-    { 0, }
-};
-
-static ctl_table rtc_root[] = {
-    { 1, "rtc", NULL, 0, 0555, rtc_table, },
-    { 0, }
-};
-
-static ctl_table dev_root[] = {
-    { CTL_DEV, "dev", NULL, 0, 0555, rtc_root, },
-    { 0, }
-};
-
-static struct ctl_table_header *sysctl_header;
-
-static int __init init_sysctl(void)
-{
-    sysctl_header = register_sysctl_table(dev_root, 0);
-    return 0;
-}
-
-static void __exit cleanup_sysctl(void)
+static void au1550_get_time(struct rtc_time *rtc_tm)
 {
-    unregister_sysctl_table(sysctl_header);
-}
+    unsigned long curr_tick;
+    unsigned char mon, leap_yr;
+    unsigned long year, trim;
+    unsigned int daysum;
+    unsigned char days_in_mon[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+    year=readl(SYS_TOYMATCH2);
+    curr_tick=readl(SYS_TOYREAD);
+    /* Check that new year did not occur right now, if so reread */
+    if ((year == 1 + readl(SYS_TOYREAD))) {
+        curr_tick=readl(SYS_TOYREAD);
+	year++; 
+    }
+
+    trim = readl(SYS_TOYTRIM); /* Divide=sys_toytrim+1, max 16 bits */
+    trim &= 0xFFFF;
+    trim++; 
+    rtc_tm->tm_year = year - epoch;
+    //        tk = curr_tick % (freq / trim); /* left over ticks */
+    curr_tick = curr_tick / (freq / trim);    /* MM-DD hh:mm:ss in seconds */
+    rtc_tm->tm_sec = curr_tick % 60;
+    curr_tick = curr_tick / 60;               /* MM-DD hh:mm in minutes */
+    rtc_tm->tm_min = curr_tick % 60;
+    curr_tick = curr_tick / 60;               /* MM-DD hh in hours */
+    rtc_tm->tm_hour = curr_tick % 24;
+    curr_tick = curr_tick / 24;               /* MM-DD in days */
+    leap_yr = ((!(year % 4) && (year % 100)) || !(year % 400));
+    days_in_mon[1] += leap_yr;                /* tm_mon is in [0;11] */
+
+    mon = 0;
+    daysum = days_in_mon[0];
+    while (curr_tick >= daysum) /* maybe add a check */
+    {
+	mon++;
+        daysum += days_in_mon[mon];
+    }
+    rtc_tm->tm_mon = mon; /* [0;11] */
+    daysum -= days_in_mon[mon];
+    curr_tick -= daysum; /* subtract days of previous months */
+    rtc_tm->tm_mday = curr_tick + 1; /* [1;31] */
+} /* au1550_get_time */
 
-/*
- *	Now all the various file operations that we export.
- */
 
-static ssize_t rtc_read(struct file *file, char *buf,
-			size_t count, loff_t *ppos)
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
-#if !RTC_IRQ
-	return -EIO;
-#else
-	DECLARE_WAITQUEUE(wait, current);
-	unsigned long data;
-	ssize_t retval;
+    struct rtc_time rtc_tm;
+    unsigned long curr_tick, wrap_tick;
+    int year; /* same type as tm_year */
+    unsigned char mon, leap_yr, tick_set;
+    unsigned long trim, rtctrim, attempts, irq_freq;
+    unsigned long cntrctrl;
+    unsigned long rtc_set_flags; /* flags must not be passed to other functions */
+    unsigned char days_in_mon[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+#ifdef RTC_DEBUG
+    unsigned short corr;
+#endif
+
+    switch (cmd) {
+    case RTC_RD_TIME: /* Read the time & date from the RTC */
+        au1550_get_time(&rtc_tm);
+
+#ifdef RTC_DEBUG
+	printk(KERN_INFO DRIVER RTC_VERSION ", time read.\n");
+#endif
+	if (copy_to_user((void *) arg, &rtc_tm, sizeof(rtc_tm))) return	-EFAULT;
+        return 0;
+    case RTC_SET_TIME: /* Set the time & date in the RTC */
+	if (!capable(CAP_SYS_TIME)) 
+	    return -EACCES;
+
+	if (copy_from_user(&rtc_tm, (struct rtc_time *) arg, sizeof(struct rtc_time))) 
+            return -EFAULT;
+
+	if ((rtc_tm.tm_year < 0))      /* disqualify tm_year before 1900 */
+	    return -EINVAL;
+	year = rtc_tm.tm_year + epoch; /* tm_year holds "years since 1900" */
+	leap_yr = ((!(year % 4) && (year % 100)) || !(year % 400));
+	days_in_mon[1] += leap_yr;                         /* tm_mon is in [0;11] */
+	if ((rtc_tm.tm_mon > 11) || (rtc_tm.tm_mday == 0)) /* tm_mon is in [0;11] */
+            return -EINVAL;
+	if (rtc_tm.tm_mday > days_in_mon[rtc_tm.tm_mon])
+	    return -EINVAL;
+	if ((rtc_tm.tm_hour >= 24) || (rtc_tm.tm_min >= 60))
+	    return -EINVAL;
+	if ((rtc_tm.tm_sec >= 62)) /* tm_sec allows double leap second */
+	    return -EINVAL;
+        
+	curr_tick = 0;
+	for (mon = 0; mon < rtc_tm.tm_mon; mon++) curr_tick += days_in_mon[mon];
+	curr_tick += rtc_tm.tm_mday - 1; /* days gone this year */
+        curr_tick *= 24;
+        curr_tick += rtc_tm.tm_hour;     /* hours gone this year */
+        curr_tick *= 60;
+        curr_tick += rtc_tm.tm_min;      /* minutes gone this year */
+        curr_tick *= 60;
+        curr_tick += rtc_tm.tm_sec;      /* seconds gone this year */
+ 	trim = readl(SYS_TOYTRIM); /* Divide=sys_toytrim+1, max 16 bits */
+	trim &= 0xFFFF;
+        trim++; 
+	curr_tick = curr_tick * (freq / trim); /* ticks gone this year */
+#ifdef RTC_DEBUG
+	printk(KERN_INFO DRIVER RTC_VERSION ", tick set %09lu.\n", curr_tick);
+#endif
 	
-	if (rtc_has_irq == 0)
-		return -EIO;
-
-	/*
-	 * Historically this function used to assume that sizeof(unsigned long)
-	 * is the same in userspace and kernelspace.  This lead to problems
-	 * for configurations with multiple ABIs such a the MIPS o32 and 64
-	 * ABIs supported on the same kernel.  So now we support read of both
-	 * 4 and 8 bytes and assume that's the sizeof(unsigned long) in the
-	 * userspace ABI.
-	 */
-	if (count != sizeof(unsigned int) && count !=  sizeof(unsigned long))
-		return -EINVAL;
-
-	add_wait_queue(&rtc_wait, &wait);
-		
-	do {
-		__set_current_state(TASK_INTERRUPTIBLE);
-
-		/* First make it right. Then make it fast. Putting this whole
-		 * block within the parentheses of a while would be too
-		 * confusing. And no, xchg() is not the answer. */
-		spin_lock_irq (&rtc_lock);
-		data = rtc_irq_data;
-		rtc_irq_data = 0;
-		spin_unlock_irq (&rtc_lock);
-
-		if (data != 0)
-			break;
-
-		if (file->f_flags & O_NONBLOCK) {
-			retval = -EAGAIN;
-			goto out;
-		}
-		if (signal_pending(current)) {
-			retval = -ERESTARTSYS;
-			goto out;
-		}
-		schedule();
-	} while (1);
-
-	if (count == sizeof(unsigned int))
-		retval = put_user(data, (unsigned int *)buf); 
-	else
-		retval = put_user(data, (unsigned long *)buf);
-	if (!retval)
-		retval = count;
- out:
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&rtc_wait, &wait);
-
-	return retval;
+	wrap_tick = 0;
+	for (mon = 0; mon < 12; mon++) wrap_tick += days_in_mon[mon];
+        wrap_tick = wrap_tick * 24 * 60 * 60 * (freq / trim); /* ticks this year */
+
+	tick_set = 0;
+	for (attempts=0; (attempts<timeout) && (tick_set==0); attempts++) {
+	    spin_lock_irqsave(&rtc_set_lock, rtc_set_flags);
+
+	    cntrctrl=readl(SYS_COUNTER_CNTRL);
+
+	    if (!(cntrctrl & SYS_CNTRCTRL_TS) && !(cntrctrl & SYS_CNTRCTRL_TM0) && !(cntrctrl & SYS_CNTRCTRL_TM2))
+	    { /* can write sys_toywrite & sys_toymatch0 */
+		writel(curr_tick, SYS_TOYWRITE);
+		writel(wrap_tick, SYS_TOYMATCH0);
+		writel(year, SYS_TOYMATCH2);
+		tick_set = 1;
+		if (got_period_irq==0) stab = 0; /* inform rtc_read */
+#ifdef RTC_DEBUG
+		printk(KERN_INFO DRIVER RTC_VERSION ", time set.\n");
 #endif
-}
-
-static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-		     unsigned long arg)
-{
-	struct rtc_time wtime; 
-
-#if RTC_IRQ
-	if (rtc_has_irq == 0) {
-		switch (cmd) {
-		case RTC_AIE_OFF:
-		case RTC_AIE_ON:
-		case RTC_PIE_OFF:
-		case RTC_PIE_ON:
-		case RTC_UIE_OFF:
-		case RTC_UIE_ON:
-		case RTC_IRQP_READ:
-		case RTC_IRQP_SET:
-			return -EINVAL;
-		};
+	    }
+	
+	    spin_unlock_irqrestore(&rtc_set_lock, rtc_set_flags);
 	}
-#endif
+	if (tick_set < 1) return -EAGAIN;
 
-	switch (cmd) {
-#if RTC_IRQ
-	case RTC_AIE_OFF:	/* Mask alarm int. enab. bit	*/
-	{
-		mask_rtc_irq_bit(RTC_AIE);
-		return 0;
-	}
-	case RTC_AIE_ON:	/* Allow alarm interrupts.	*/
-	{
-		set_rtc_irq_bit(RTC_AIE);
-		return 0;
-	}
-	case RTC_PIE_OFF:	/* Mask periodic int. enab. bit	*/
-	{
-		mask_rtc_irq_bit(RTC_PIE);
-		if (rtc_status & RTC_TIMER_ON) {
-			spin_lock_irq (&rtc_lock);
-			rtc_status &= ~RTC_TIMER_ON;
-			del_timer(&rtc_irq_timer);
-			spin_unlock_irq (&rtc_lock);
-		}
-		return 0;
-	}
-	case RTC_PIE_ON:	/* Allow periodic ints		*/
-	{
+	return 0;
+    case RTC_IRQP_SET: /* Set periodic irq frequency */
+        if (got_period_irq == 0) return -EINVAL;
 
-		/*
-		 * We don't really want Joe User enabling more
-		 * than 64Hz of interrupts on a multi-user machine.
-		 */
-		if ((rtc_freq > rtc_max_user_freq) && 
-		    (!capable(CAP_SYS_RESOURCE)))
-			return -EACCES;
-
-		if (!(rtc_status & RTC_TIMER_ON)) {
-			spin_lock_irq (&rtc_lock);
-			rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
-			add_timer(&rtc_irq_timer);
-			rtc_status |= RTC_TIMER_ON;
-			spin_unlock_irq (&rtc_lock);
-		}
-		set_rtc_irq_bit(RTC_PIE);
-		return 0;
-	}
-	case RTC_UIE_OFF:	/* Mask ints from RTC updates.	*/
-	{
-		mask_rtc_irq_bit(RTC_UIE);
-		return 0;
-	}
-	case RTC_UIE_ON:	/* Allow ints for RTC updates.	*/
-	{
-		set_rtc_irq_bit(RTC_UIE);
-		return 0;
-	}
+        /* arg is an integer, accept two values for some fractional f */
+        switch (arg) { /* has to be rewritten if clock freq is changed */
+  	    case     1000: rtctrim =  0; break; /* f=clock/(rtctrim+1)=1000 */
+   	    case      500: rtctrim =  1; break; /* f=clock/(rtctrim+1)=500 */
+  	    case      334: 
+  	    case      333: rtctrim =  2; break; /* f=clock/(rtctrim+1)=333.3 */
+   	    case      250: rtctrim =  3; break; /* f=clock/(rtctrim+1)=250 */
+   	    case      200: rtctrim =  4; break; /* f=clock/(rtctrim+1)=200 */
+  	    case      167: 
+  	    case      166: rtctrim =  5; break; /* f=clock/(rtctrim+1)=166.7 */
+  	    case      143: 
+  	    case      142: rtctrim =  6; break; /* f=clock/(rtctrim+1)=142.8 */
+   	    case      125: rtctrim =  7; break; /* f=clock/(rtctrim+1)=125 */
+  	    case      112:
+  	    case      111: rtctrim =  8; break; /* f=clock/(rtctrim+1)=111.1 */
+   	    case      100: rtctrim =  9; break; /* f=clock/(rtctrim+1)=100 */
+  	    case       91:
+  	    case       90: rtctrim = 10; break; /* f=clock/(rtctrim+1)=90.9 */
+  	    case       84:
+  	    case       83: rtctrim = 11; break; /* f=clock/(rtctrim+1)=83.3 */
+  	    case       77:
+  	    case       76: rtctrim = 12; break; /* f=clock/(rtctrim+1)=76.9 */
+  	    case       72:
+  	    case       71: rtctrim = 13; break; /* f=clock/(rtctrim+1)=71.4 */
+  	    case       67:
+  	    case       66: rtctrim = 14; break; /* f=clock/(rtctrim+1)=66.7 */
+  	    case       63:
+  	    case       62: rtctrim = 15; break; /* f=clock/(rtctrim+1)=62.5 */
+  	    case       59:
+  	    case       58: rtctrim = 16; break; /* f=clock/(rtctrim+1)=58.8 */
+  	    case       56:
+  	    case       55: rtctrim = 17; break; /* f=clock/(rtctrim+1)=55.5 */
+  	    case       53:
+  	    case       52: rtctrim = 18; break; /* f=clock/(rtctrim+1)=52.6 */
+   	    case       50: rtctrim = 19; break; /* f=clock/(rtctrim+1)=50 */
+  	    case       48:
+  	    case       47: rtctrim = 20; break; /* f=clock/(rtctrim+1)=47.6 */
+  	    case       46:
+  	    case       45: rtctrim = 21; break; /* f=clock/(rtctrim+1)=45.5 */
+  	    case       44:
+  	    case       43: rtctrim = 22; break; /* f=clock/(rtctrim+1)=43.5 */
+  	    case       42:
+  	    case       41: rtctrim = 23; break; /* f=clock/(rtctrim+1)=41.6 */
+  	    case       40: rtctrim = 24; break; /* f=clock/(rtctrim+1)=40 */
+  	    case       39:
+  	    case       38: rtctrim = 25; break; /* f=clock/(rtctrim+1)=38.5 */
+  	    case       37: rtctrim = 26; break; /* f=clock/(rtctrim+1)=37.04 */
+  	    case       36: rtctrim = 27; break; /* f=clock/(rtctrim+1)=35.71 */
+  	    case       35:
+  	    case       34: rtctrim = 28; break; /* f=clock/(rtctrim+1)=34.48 */
+  	    case       33: rtctrim = 29; break; /* f=clock/(rtctrim+1)=33.33 */
+  	    case       32: rtctrim = 30; break; /* f=clock/(rtctrim+1)=32.25 */
+  	    case       31: rtctrim = 31; break; /* f=clock/(rtctrim+1)=31.25 */
+  	    case       30: rtctrim = 32; break; /* f=clock/(rtctrim+1)=30.30 */
+  	    case       29: rtctrim = 33; break; /* f=clock/(rtctrim+1)=29.41 */
+  	    case       28: rtctrim = 35; break; /* f=clock/(rtctrim+1)=27.77 */
+  	    case       27: rtctrim = 36; break; /* f=clock/(rtctrim+1)=27.03 */
+  	    case       26: rtctrim = 37; break; /* f=clock/(rtctrim+1)=26.32 */
+  	    case       25: rtctrim = 39; break; /* f=clock/(rtctrim+1)=25 */
+  	    case       24: rtctrim = 41; break; /* f=clock/(rtctrim+1)=23.81 */
+  	    case       23: rtctrim = 42; break; /* f=clock/(rtctrim+1)=23.26 */
+  	    case       22: rtctrim = 44; break; /* f=clock/(rtctrim+1)=22.22 */
+  	    case       21: rtctrim = 47; break; /* f=clock/(rtctrim+1)=20.83 */
+  	    case       20: rtctrim = 49; break; /* f=clock/(rtctrim+1)=20 */
+  	    case       19: rtctrim = 52; break; /* f=clock/(rtctrim+1)=18.87 */
+  	    case       18: rtctrim = 55; break; /* f=clock/(rtctrim+1)=17.86 */
+  	    case       17: rtctrim = 58; break; /* f=clock/(rtctrim+1)=16.95 */
+  	    case       16: rtctrim = 62; break; /* f=clock/(rtctrim+1)=15.87 */
+  	    case       15: rtctrim = 66; break; /* f=clock/(rtctrim+1)=14.93 */
+  	    case       14: rtctrim = 70; break; /* f=clock/(rtctrim+1)=14.09 */
+  	    case       13: rtctrim = 76; break; /* f=clock/(rtctrim+1)=12.99 */
+  	    case       12: rtctrim = 82; break; /* f=clock/(rtctrim+1)=12.05 */
+  	    case       11: rtctrim = 90; break; /* f=clock/(rtctrim+1)=10.99 */
+  	    case       10: rtctrim = 99; break; /* f=clock/(rtctrim+1)=10 */
+  	    case        9: rtctrim =110; break; /* f=clock/(rtctrim+1)=9.009 */
+  	    case        8: rtctrim =124; break; /* f=clock/(rtctrim+1)=8 */
+  	    case        7: rtctrim =142; break; /* f=clock/(rtctrim+1)=6.993 */
+  	    case        6: rtctrim =166; break; /* f=clock/(rtctrim+1)=5.988 */
+  	    case        5: rtctrim =199; break; /* f=clock/(rtctrim+1)=5 */
+  	    case        4: rtctrim =249; break; /* f=clock/(rtctrim+1)=4 */
+  	    case        3: rtctrim =332; break; /* f=clock/(rtctrim+1)=3.003 */
+  	    case        2: rtctrim =499; break; /* f=clock/(rtctrim+1)=2 */
+  	    case        1: rtctrim =999; break; /* f=clock/(rtctrim+1)=1 */
+	    default: {
+//#ifdef RTC_DEBUG
+	        printk(KERN_WARNING DRIVER RTC_VERSION ", Invalid periodic freq.\n");
+	        printk(KERN_WARNING "Valid values are 1000/n rounded to an integer > 0.\n");
+//#endif
+                return -EDOM; // -EINVAL;
+	    }
+	}
+
+	if ((arg > 64) && !(capable(CAP_SYS_RESOURCE))) return -EACCES;
+
+	tick_set = 0;
+	for (attempts=0; (attempts<timeout) && (tick_set==0); attempts++) {
+//	    spin_lock_irqsave(&rtc_set_lock, rtc_set_flags);
+
+	    cntrctrl=readl(SYS_COUNTER_CNTRL);
+	    if (!(cntrctrl & SYS_CNTRCTRL_RTS))
+	    { /* can write sys_rtctrim */
+		writel(rtctrim, SYS_RTCTRIM);
+		tick_set = 1;
+#ifdef RTC_DEBUG
+		corr=freq/(rtctrim+1);
+		printk(KERN_INFO DRIVER RTC_VERSION ", rtc trim set to %lu.\n", rtctrim);
+		printk(KERN_INFO DRIVER RTC_VERSION ", corresponding f=%u.%03lu Hz.\n", 
+                    corr, ((freq-corr*(rtctrim+1))*1000)/(rtctrim+1));
 #endif
-	case RTC_ALM_READ:	/* Read the present alarm time */
-	{
-		/*
-		 * This returns a struct rtc_time. Reading >= 0xc0
-		 * means "don't care" or "match all". Only the tm_hour,
-		 * tm_min, and tm_sec values are filled in.
-		 */
-		memset(&wtime, 0, sizeof(struct rtc_time));
-		get_rtc_alm_time(&wtime);
-		break; 
+		stab = 0; /* inform rtc_read */
+	    }
+	
+//	    spin_unlock_irqrestore(&rtc_set_lock, rtc_set_flags);
 	}
-	case RTC_ALM_SET:	/* Store a time into the alarm */
-	{
-		/*
-		 * This expects a struct rtc_time. Writing 0xff means
-		 * "don't care" or "match all". Only the tm_hour,
-		 * tm_min and tm_sec are used.
-		 */
-		unsigned char hrs, min, sec;
-		struct rtc_time alm_tm;
-
-		if (copy_from_user(&alm_tm, (struct rtc_time*)arg,
-				   sizeof(struct rtc_time)))
-			return -EFAULT;
-
-		hrs = alm_tm.tm_hour;
-		min = alm_tm.tm_min;
-		sec = alm_tm.tm_sec;
-
-		spin_lock_irq(&rtc_lock);
-		if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ||
-		    RTC_ALWAYS_BCD)
-		{
-			if (sec < 60) BIN_TO_BCD(sec);
-			else sec = 0xff;
-
-			if (min < 60) BIN_TO_BCD(min);
-			else min = 0xff;
-
-			if (hrs < 24) BIN_TO_BCD(hrs);
-			else hrs = 0xff;
-		}
-		CMOS_WRITE(hrs, RTC_HOURS_ALARM);
-		CMOS_WRITE(min, RTC_MINUTES_ALARM);
-		CMOS_WRITE(sec, RTC_SECONDS_ALARM);
-		spin_unlock_irq(&rtc_lock);
+	if (tick_set < 1) return -EAGAIN;
 
-		return 0;
-	}
-	case RTC_RD_TIME:	/* Read the time/date from RTC	*/
-	{
-		memset(&wtime, 0, sizeof(struct rtc_time));
-		get_rtc_time(&wtime);
-		break;
+	return 0;
+    case RTC_IRQP_READ: /* Read periodic irq frequency */
+ 	rtctrim = readl(SYS_RTCTRIM); /* Divide=sys_rtctrim+1, max 16 bits */
+	rtctrim &= 0xFFFF;
+        rtctrim++;
+	irq_freq = freq / rtctrim;
+// 	trim = readl(SYS_TOYTRIM); /* Divide=sys_toytrim+1, max 16 bits */
+//	trim &= 0xFFFF;
+//      trim++;
+//	irq_freq = freq / trim;
+#ifdef RTC_DEBUG
+	printk(KERN_INFO DRIVER RTC_VERSION ", reported freq %01lu.\n", irq_freq);
+#endif
+	return put_user(irq_freq, (unsigned long *) arg);
+    case RTC_PIE_ON: /* Enable periodic irqs */
+        if (got_period_irq == 1) {
+
+	    rtctrim = readl(SYS_RTCTRIM); /* Divide=sys_rtctrim+1, max 16 bits */
+	    rtctrim &= 0xFFFF;
+	    rtctrim++;
+	    if (((freq/rtctrim) > 64) && !(capable(CAP_SYS_RESOURCE))) return -EACCES;
+
+	    writel(0x00040000, IC0_MASKSET);   /* rtc tick, enable irq in PIC */
+#ifdef RTC_DEBUG
+	    printk(KERN_INFO DRIVER RTC_VERSION ", periodic irq enabled.\n");
+#endif
+	} else if (got_tick_irq ==1) { /* fallback to fixed f=125 Hz */
+	    trim = readl(SYS_TOYTRIM); /* Divide=sys_toytrim+1, max 16 bits */
+	    trim &= 0xFFFF;
+	    trim++;
+	    if (((freq/trim) > 64) && !(capable(CAP_SYS_RESOURCE))) return -EACCES;
+
+	    writel(0x00004000, IC0_MASKSET);   /* toy tick, enable irq in PIC */
+#ifdef RTC_DEBUG
+	    printk(KERN_INFO DRIVER RTC_VERSION ", periodic/tick irq enabled.\n");
+#endif
+	} else {
+	    printk(KERN_WARNING DRIVER RTC_VERSION ", periodic irq not available.\n");
+	    return -EINVAL;
 	}
-	case RTC_SET_TIME:	/* Set the RTC */
-	{
-		struct rtc_time rtc_tm;
-		unsigned char mon, day, hrs, min, sec, leap_yr;
-		unsigned char save_control, save_freq_select;
-		unsigned int yrs;
-#ifdef CONFIG_DECSTATION
-		unsigned int real_yrs;
-#endif
-
-		if (!capable(CAP_SYS_TIME))
-			return -EACCES;
-
-		if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
-				   sizeof(struct rtc_time)))
-			return -EFAULT;
-
-		yrs = rtc_tm.tm_year + 1900;
-		mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
-		day = rtc_tm.tm_mday;
-		hrs = rtc_tm.tm_hour;
-		min = rtc_tm.tm_min;
-		sec = rtc_tm.tm_sec;
-
-		if (yrs < 1970)
-			return -EINVAL;
-
-		leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
-
-		if ((mon > 12) || (day == 0))
-			return -EINVAL;
-
-		if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
-			return -EINVAL;
-			
-		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
-			return -EINVAL;
-
-		if ((yrs -= epoch) > 255)    /* They are unsigned */
-			return -EINVAL;
-
-		spin_lock_irq(&rtc_lock);
-#ifdef CONFIG_DECSTATION
-		real_yrs = yrs;
-		yrs = 72;
-
-		/*
-		 * We want to keep the year set to 73 until March
-		 * for non-leap years, so that Feb, 29th is handled
-		 * correctly.
-		 */
-		if (!leap_yr && mon < 3) {
-			real_yrs--;
-			yrs = 73;
-		}
-#endif
-		/* These limits and adjustments are independant of
-		 * whether the chip is in binary mode or not.
-		 */
-		if (yrs > 169) {
-			spin_unlock_irq(&rtc_lock);
-			return -EINVAL;
-		}
-		if (yrs >= 100)
-			yrs -= 100;
-
-		if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
-		    || RTC_ALWAYS_BCD) {
-			BIN_TO_BCD(sec);
-			BIN_TO_BCD(min);
-			BIN_TO_BCD(hrs);
-			BIN_TO_BCD(day);
-			BIN_TO_BCD(mon);
-			BIN_TO_BCD(yrs);
-		}
-
-		save_control = CMOS_READ(RTC_CONTROL);
-		CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
-		save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
-		CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
-
-#ifdef CONFIG_DECSTATION
-		CMOS_WRITE(real_yrs, RTC_DEC_YEAR);
-#endif
-		CMOS_WRITE(yrs, RTC_YEAR);
-		CMOS_WRITE(mon, RTC_MONTH);
-		CMOS_WRITE(day, RTC_DAY_OF_MONTH);
-		CMOS_WRITE(hrs, RTC_HOURS);
-		CMOS_WRITE(min, RTC_MINUTES);
-		CMOS_WRITE(sec, RTC_SECONDS);
-
-		CMOS_WRITE(save_control, RTC_CONTROL);
-		CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 
-		spin_unlock_irq(&rtc_lock);
-		return 0;
-	}
-#if RTC_IRQ
-	case RTC_IRQP_READ:	/* Read the periodic IRQ rate.	*/
-	{
-		return put_user(rtc_freq, (unsigned long *)arg);
-	}
-	case RTC_IRQP_SET:	/* Set periodic IRQ rate.	*/
-	{
-		int tmp = 0;
-		unsigned char val;
-
-		/* 
-		 * The max we can do is 8192Hz.
-		 */
-		if ((arg < 2) || (arg > 8192))
-			return -EINVAL;
-		/*
-		 * We don't really want Joe User generating more
-		 * than 64Hz of interrupts on a multi-user machine.
-		 */
-		if ((arg > rtc_max_user_freq) && (!capable(CAP_SYS_RESOURCE)))
-			return -EACCES;
-
-		while (arg > (1<<tmp))
-			tmp++;
-
-		/*
-		 * Check that the input was really a power of 2.
-		 */
-		if (arg != (1<<tmp))
-			return -EINVAL;
-
-		spin_lock_irq(&rtc_lock);
-		rtc_freq = arg;
-
-		val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
-		val |= (16 - tmp);
-		CMOS_WRITE(val, RTC_FREQ_SELECT);
-		spin_unlock_irq(&rtc_lock);
-		return 0;
-	}
-#endif
-	case RTC_EPOCH_READ:	/* Read the epoch.	*/
-	{
-		return put_user (epoch, (unsigned long *)arg);
+	return 0;
+    case RTC_PIE_OFF: /* Disable periodic irqs */
+        if (got_period_irq == 1) { 
+            writel(0x00040000, IC0_MASKCLR); /* rtc tick, disable irq in PIC */
+#ifdef RTC_DEBUG
+	    printk(KERN_INFO DRIVER RTC_VERSION ", periodic irq disabled.\n");
+#endif
+	} else if (got_tick_irq ==1) { /* fallback to fixed f=125 Hz */
+            writel(0x00004000, IC0_MASKCLR); /* toy tick, disable irq in PIC */
+#ifdef RTC_DEBUG
+	    printk(KERN_INFO DRIVER RTC_VERSION ", periodic/tick irq disabled.\n");
+#endif
+	} else {
+	    printk(KERN_WARNING DRIVER RTC_VERSION ", periodic irq not available.\n");
+	    return -EINVAL;
 	}
-	case RTC_EPOCH_SET:	/* Set the epoch.	*/
-	{
-		/* 
-		 * There were no RTC clocks before 1900.
-		 */
-		if (arg < 1900)
-			return -EINVAL;
-
-		if (!capable(CAP_SYS_TIME))
-			return -EACCES;
 
-		epoch = arg;
-		return 0;
-	}
-	default:
-		return -ENOTTY;
-	}
-	return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
-}
+	return 0;
+//    case RTC_AIE_ON: /* Enable alarm irqs */
+    case RTC_UIE_ON: /* Enable RTC irqs */
+        if (got_tick_irq == 0) return -EINVAL;
+
+ 	trim = readl(SYS_TOYTRIM); /* Divide=sys_toytrim+1, max 16 bits */
+	trim &= 0xFFFF;
+        trim++;
+	if (((freq / trim) > 64) && !(capable(CAP_SYS_RESOURCE))) return -EACCES;
+
+	writel(0x00004000, IC0_MASKSET);   /* toy tick, enable irq in PIC */	
+#ifdef RTC_DEBUG
+	printk(KERN_INFO DRIVER RTC_VERSION ", tick irq enabled.\n");
+#endif
+	
+	return 0;
+//    case RTC_AIE_OFF: /* Disable alarm irqs */
+    case RTC_UIE_OFF: /* Disable RTC irqs */
+        if (got_tick_irq == 0) return -EINVAL;
+
+        writel(0x00004000, IC0_MASKCLR); /* toy tick, disable irq in PIC */
+#ifdef RTC_DEBUG
+	printk(KERN_INFO DRIVER RTC_VERSION ", tick irq disabled.\n");
+#endif
+ 
+	return 0;
+    default:
+	printk(KERN_WARNING DRIVER RTC_VERSION 
+            ", invalid or unimplemented ioctl command.\n");
+	return -EINVAL;
+    }
+} /* rtc_ioctl */
 
-/*
- *	We enforce only one user at a time here with the open/close.
- *	Also clear the previous interrupt data on an open, and clean
- *	up things on a close.
- */
 
-/* We use rtc_lock to protect against concurrent opens. So the BKL is not
- * needed here. Or anywhere else in this driver. */
-static int rtc_open(struct inode *inode, struct file *file)
+static void rtc_tick_handler(int irq, void *dev_id, struct pt_regs *cpuregs)
 {
-	spin_lock_irq (&rtc_lock);
-
-	if(rtc_status & RTC_IS_OPEN)
-		goto out_busy;
+    writel(0x00040000, IC0_RISINGCLR); /* rtc tick, clr rising edge detect */
 
-	rtc_status |= RTC_IS_OPEN;
+    new_eon=readl(SYS_RTCMATCH2);
+    new_pcnt=readl(SYS_RTCREAD);
+    /* Check that new eon did not occur right now, if so reread */
+    if ((new_eon == 1 + readl(SYS_RTCREAD))) {
+        new_pcnt=readl(SYS_RTCREAD);
+	new_eon++; 
+    }
+    wake_up_interruptible(&period_q);
+} /* rtc_tick_handler */
 
-	rtc_irq_data = 0;
-	spin_unlock_irq (&rtc_lock);
-	return 0;
-
-out_busy:
-	spin_unlock_irq (&rtc_lock);
-	return -EBUSY;
-}
-
-static int rtc_fasync (int fd, struct file *filp, int on)
 
+static void toy_tick_handler(int irq, void *dev_id, struct pt_regs *cpuregs)
 {
-	return fasync_helper (fd, filp, on, &rtc_async_queue);
-}
+    writel(0x00004000, IC0_RISINGCLR); /* toy tick, clr rising edge detect */
 
-static int rtc_release(struct inode *inode, struct file *file)
-{
-#if RTC_IRQ
-	unsigned char tmp;
+    if (got_period_irq == 0) { /* fallback to fixed f=125 Hz */
+        new_year=readl(SYS_TOYMATCH2);
+        new_cnt=readl(SYS_TOYREAD);
+        /* Check that new year did not occur right now, if so reread */
+        if ((new_year == 1 + readl(SYS_TOYREAD))) {
+            new_cnt=readl(SYS_TOYREAD);
+    	    new_year++; 
+        }
+        wake_up_interruptible(&period_q);
+    }
+} /* toy_tick_handler */
 
-	if (rtc_has_irq == 0)
-		goto no_irq;
 
-	/*
-	 * Turn off all interrupts once the device is no longer
-	 * in use, and clear the data.
-	 */
-
-	spin_lock_irq(&rtc_lock);
-	tmp = CMOS_READ(RTC_CONTROL);
-	tmp &=  ~RTC_PIE;
-	tmp &=  ~RTC_AIE;
-	tmp &=  ~RTC_UIE;
-	CMOS_WRITE(tmp, RTC_CONTROL);
-	CMOS_READ(RTC_INTR_FLAGS);
-
-	if (rtc_status & RTC_TIMER_ON) {
-		rtc_status &= ~RTC_TIMER_ON;
-		del_timer(&rtc_irq_timer);
-	}
-	spin_unlock_irq(&rtc_lock);
+static void eon_elapsed_handler(int irq, void *dev_id, struct pt_regs *cpuregs)
+{
+    unsigned long eon;
+    unsigned long curr_tick, wrap_tick;
+    unsigned long cntrctrl;
+
+//  if (0x00080000 & readl(IC0_RISINGRD)) {
+        writel(0x00080000, IC0_RISINGCLR); /* rtc match 0, clr rising edge detect */
+
+	eon = readl(SYS_RTCMATCH2);
+        wrap_tick = readl(SYS_RTCMATCH0);
+
+	eon++;
+	cntrctrl=readl(SYS_COUNTER_CNTRL);
+	if (cntrctrl & SYS_CNTRCTRL_RM2) write_error++;
+	curr_tick=readl(SYS_RTCREAD);
+	if (curr_tick < wrap_tick) {
+	    spurious_irq++; /* for debug purposes */
+        } else {
+	    /* Write to sys_rtcmatch2 without checks */
+	    writel(eon, SYS_RTCMATCH2);
+	    eon_irq++; /* for debug purposes */
+	}    
+//  } /* else not our interrupt */
+//  else {
+//      spurious_irq++; /* for debug purposes */
+//  }
+} /* eon_elapsed_handler */
 
-	if (file->f_flags & FASYNC) {
-		rtc_fasync (-1, file, 0);
-	}
-no_irq:
-#endif
 
-	spin_lock_irq (&rtc_lock);
-	rtc_irq_data = 0;
-	spin_unlock_irq (&rtc_lock);
+static void year_elapsed_handler(int irq, void *dev_id, struct pt_regs *cpuregs)
+{
+    unsigned long year;
+    unsigned long curr_tick, wrap_tick;
+    unsigned char leap_yr;
+    unsigned long trim;
+    unsigned long cntrctrl;
+
+#ifdef RTC_DEBUG
+    cp0_cause = cpuregs->cp0_cause;
+    cp0_status = cpuregs->cp0_status;
+//    ic0_risingrd = readl(IC0_RISINGRD); /* Kernel seems to have cleared it already */
+//    ic0_fallingrd = readl(IC0_FALLINGRD);
+//    ic0_req0int = readl(IC0_REQ0INT);
+#endif
+
+//  if (0x00008000 & readl(IC0_RISINGRD)) {
+        writel(0x00008000, IC0_RISINGCLR); /* match 0, clr rising edge detect */
+
+ 	trim = readl(SYS_TOYTRIM); /* Divide=sys_toytrim+1, max 16 bits */
+	trim &= 0xFFFF;
+        trim++; 
+	year = readl(SYS_TOYMATCH2);
+	leap_yr = ((!(year % 4) && (year % 100)) || !(year % 400));
+	wrap_tick = yearly_seconds_normal + leap_yr * addl_seconds_leap;
+        wrap_tick = wrap_tick * (freq / trim); /* ticks this year */
+
+	year++;
+	cntrctrl=readl(SYS_COUNTER_CNTRL);
+	if (!(!(cntrctrl & SYS_CNTRCTRL_TS) && !(cntrctrl & SYS_CNTRCTRL_TM0) && !(cntrctrl & SYS_CNTRCTRL_TM2)))
+ 	    write_error++;
+	curr_tick=readl(SYS_TOYREAD);
+	if (curr_tick < wrap_tick) {
+	    spurious_irq++; /* for debug purposes */
+        } else {
+	    curr_tick -= wrap_tick; /* if delayed by other interrupt */
+	    /* Write to sys_toywrite without checks, might collide with RTC_SET_TIME */
+	    writel(curr_tick, SYS_TOYWRITE);
+	    /* Write to sys_toymatch2 without checks, might collide with RTC_SET_TIME */
+	    writel(year, SYS_TOYMATCH2);
+	    leap_yr = ((!(year % 4) && (year % 100)) || !(year % 400));
+	    wrap_tick = yearly_seconds_normal + leap_yr * addl_seconds_leap;
+	    wrap_tick = wrap_tick * (freq / trim); /* ticks this year */
+	    /* Write to sys_toymatch0 without checks, might collide with RTC_SET_TIME */
+	    writel(wrap_tick, SYS_TOYMATCH0);
+	    year_irq++; /* for debug purposes */
+	}    
+//  } /* else not our interrupt */
+//  else {
+//      spurious_irq++; /* for debug purposes */
+//  }
+} /* year_elapsed_handler */
 
-	/* No need for locking -- nobody else can do anything until this rmw is
-	 * committed, and no timer is running. */
-	rtc_status &= ~RTC_IS_OPEN;
-	return 0;
-}
 
-#if RTC_IRQ
-/* Called without the kernel lock - fine */
-static unsigned int rtc_poll(struct file *file, poll_table *wait)
+static int rtc_open(struct inode *inode, struct file *file)
+/* spinlock is used against, two processes getting the idea to open concurrently. */
 {
-	unsigned long l;
-
-	if (rtc_has_irq == 0)
-		return 0;
+    unsigned long rtc_open_flags; /* flags must not be passed to other functions */
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", open started.\n");
+#endif
+    spin_lock_irqsave(&rtc_open_lock, rtc_open_flags);
 
-	poll_wait(file, &rtc_wait, wait);
+    if (rtc_status & RTC_IS_OPEN) {
+	spin_unlock_irqrestore(&rtc_open_lock, rtc_open_flags);
+	return -EBUSY;
+    }
 
-	spin_lock_irq (&rtc_lock);
-	l = rtc_irq_data;
-	spin_unlock_irq (&rtc_lock);
+    rtc_status |= RTC_IS_OPEN;
 
-	if (l != 0)
-		return POLLIN | POLLRDNORM;
-	return 0;
-}
+    old_year=readl(SYS_TOYMATCH2);
+    old_cnt=readl(SYS_TOYREAD);
+    /* Check that new year did not occur right now, if so reread */
+    if (old_year == 1 + readl(SYS_TOYREAD)) {
+        old_cnt=readl(SYS_TOYREAD);
+	old_year++; 
+    }
+    old_eon=readl(SYS_RTCMATCH2);
+    old_pcnt=readl(SYS_RTCREAD);
+    /* Check that new eon did not occur right now, if so reread */
+    if (old_eon == 1 + readl(SYS_RTCREAD)) {
+        old_pcnt=readl(SYS_RTCREAD);
+	old_eon++; 
+    }
+    stab=1; 
+
+    spin_unlock_irqrestore(&rtc_open_lock, rtc_open_flags);
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", open finished.\n");
 #endif
+    return 0;
+} /* rtc_open */
 
-/*
- *	The various file operations we support.
- */
-
-static struct file_operations rtc_fops = {
-	owner:		THIS_MODULE,
-	llseek:		no_llseek,
-	read:		rtc_read,
-#if RTC_IRQ
-	poll:		rtc_poll,
-#endif
-	ioctl:		rtc_ioctl,
-	open:		rtc_open,
-	release:	rtc_release,
-	fasync:		rtc_fasync,
-};
 
-static struct miscdevice rtc_dev=
+static int rtc_release(struct inode *inode, struct file *file)
 {
-	RTC_MINOR,
-	"rtc",
-	&rtc_fops
-};
+    unsigned long rtc_restore_flags; /* flags must not be passed to other functions */
 
-static int __init rtc_init(void)
-{
-#if defined(__alpha__) || defined(__mips__)
-	unsigned int year, ctrl;
-	unsigned long uip_watchdog;
-	char *guess = NULL;
-#endif
-#ifdef __sparc__
-	struct linux_ebus *ebus;
-	struct linux_ebus_device *edev;
-#ifdef __sparc_v9__
-	struct isa_bridge *isa_br;
-	struct isa_device *isa_dev;
-#endif
-#endif
-#ifndef __sparc__
-	void *r;
-#endif
-
-#ifdef __sparc__
-	for_each_ebus(ebus) {
-		for_each_ebusdev(edev, ebus) {
-			if(strcmp(edev->prom_name, "rtc") == 0) {
-				rtc_port = edev->resource[0].start;
-				rtc_irq = edev->irqs[0];
-				goto found;
-			}
-		}
-	}
-#ifdef __sparc_v9__
-	for_each_isa(isa_br) {
-		for_each_isadev(isa_dev, isa_br) {
-			if (strcmp(isa_dev->prom_name, "rtc") == 0) {
-				rtc_port = isa_dev->resource.start;
-				rtc_irq = isa_dev->irq;
-				goto found;
-			}
-		}
-	}
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", release started.\n");
 #endif
-	printk(KERN_ERR "rtc_init: no PC rtc found\n");
-	return -EIO;
-
-found:
-	if (rtc_irq == PCI_IRQ_NONE) {
-		rtc_has_irq = 0;
-		goto no_irq;
-	}
+    spin_lock_irqsave(&rtc_open_lock, rtc_restore_flags);
 
-	/*
-	 * XXX Interrupt pin #7 in Espresso is shared between RTC and
-	 * PCI Slot 2 INTA# (and some INTx# in Slot 1). SA_INTERRUPT here
-	 * is asking for trouble with add-on boards. Change to SA_SHIRQ.
-	 */
-	if (request_irq(rtc_irq, rtc_interrupt, SA_INTERRUPT, "rtc", (void *)&rtc_port)) {
-		/*
-		 * Standard way for sparc to print irq's is to use
-		 * __irq_itoa(). I think for EBus it's ok to use %d.
-		 */
-		printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);
-		return -EIO;
-	}
-no_irq:
-#else
-	if (RTC_IOMAPPED)
-		r = request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
-	else
-		r = request_mem_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
-	if (!r) {
-		printk(KERN_ERR "rtc: I/O resource %lx is not free.\n",
-		       (long)(RTC_PORT(0)));
-		return -EIO;
-	}
+    rtc_status &= ~RTC_IS_OPEN;
 
-#if RTC_IRQ
-	if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL)) {
-		/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
-		printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
-		if (RTC_IOMAPPED)
-			release_region(RTC_PORT(0), RTC_IO_EXTENT);
-		else
-			release_mem_region(RTC_PORT(0), RTC_IO_EXTENT);
-		return -EIO;
-	}
+    spin_unlock_irqrestore(&rtc_open_lock, rtc_restore_flags);
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", release finished.\n");
 #endif
+    return 0;
+} /* rtc_release */
 
-#endif /* __sparc__ vs. others */
-
-	misc_register(&rtc_dev);
-	create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL);
 
-#if defined(__alpha__) || defined(__mips__)
-	rtc_freq = HZ;
-	
-	/* Each operating system on an Alpha uses its own epoch.
-	   Let's try to guess which one we are using now. */
-	
-	uip_watchdog = jiffies;
-	if (rtc_is_updating() != 0)
-		while (jiffies - uip_watchdog < 2*HZ/100) { 
-			barrier();
-			cpu_relax();
-		}
-	
-	spin_lock_irq(&rtc_lock);
-	year = CMOS_READ(RTC_YEAR);
-	ctrl = CMOS_READ(RTC_CONTROL);
-	spin_unlock_irq(&rtc_lock);
-	
-	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
-		BCD_TO_BIN(year);       /* This should never happen... */
-	
-	if (year < 20) {
-		epoch = 2000;
-		guess = "SRM (post-2000)";
-	} else if (year >= 20 && year < 48) {
-		epoch = 1980;
-		guess = "ARC console";
-	} else if (year >= 48 && year < 72) {
-		epoch = 1952;
-		guess = "Digital UNIX";
-#if defined(__mips__)
-	} else if (year >= 72 && year < 74) {
-		epoch = 2000;
-		guess = "Digital DECstation";
-#else
-	} else if (year >= 70) {
-		epoch = 1900;
-		guess = "Standard PC (1900)";
-#endif
-	}
-	if (guess)
-		printk(KERN_INFO "rtc: %s epoch (%lu) detected\n", guess, epoch);
-#endif
-#if RTC_IRQ
-	if (rtc_has_irq == 0)
-		goto no_irq2;
-
-	init_timer(&rtc_irq_timer);
-	rtc_irq_timer.function = rtc_dropped_irq;
-	spin_lock_irq(&rtc_lock);
-	/* Initialize periodic freq. to CMOS reset default, which is 1024Hz */
-	CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT);
-	spin_unlock_irq(&rtc_lock);
-	rtc_freq = 1024;
-no_irq2:
+static ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{ /* rtc_read is NOT re-entrant */
+    ssize_t result;
+    unsigned long curr_tick, curr_year;
+    unsigned long curr_ptick, curr_eon;
+    //    float jiffies_per_tick;
+    unsigned long trim;
+    unsigned long wrap_tick;
+    unsigned long wrap_ptick = 0xFFFFFFFF;
+    unsigned char leap_yr;
+
+#ifdef RTC_DEBUG
+    sometimes = (sometimes + 1) % 2500; /* How often to print, % 1 -> never */
+    //    sometimes = 1; /* always */
+    if (sometimes == 1 ) 
+        printk(KERN_INFO DRIVER RTC_VERSION ", read started.\n");
+#endif
+
+    if ((got_period_irq == 0) && (got_tick_irq == 0)) return -EIO;
+
+    //    init_waitqueue_head (&period_q);
+
+    if (got_period_irq == 1) {
+        curr_ptick=readl(SYS_RTCREAD);
+#ifdef RTC_DEBUG
+	if (sometimes == 1 ) 
+            printk(KERN_INFO "RTC: process %i (%s) is going to sleep at periodic tick %lu.\n", 
+	        current->pid, current->comm, curr_ptick);
+	sleep_diff=curr_ptick-old_pcnt;
+#endif
+//    wait_event_interruptible(period_q, (new_cnt>old_cnt));
+//    wait_event_interruptible(period_q, ((new_year>old_year) || (new_cnt>old_cnt)));
+//    wait_event_interruptible(period_q, ((new_year>old_year) || (new_cnt>old_cnt) || (stab==0)));
+	wait_event_interruptible(period_q, ((new_eon!=old_eon) || (new_pcnt!=old_pcnt) || (stab==0)));
+
+	curr_eon=readl(SYS_RTCMATCH2);
+	curr_ptick=readl(SYS_RTCREAD);
+        /* Check that new eon did not occur right now, if so reread */
+	if ((curr_eon == 1 + readl(SYS_RTCREAD))) {
+	    curr_ptick=readl(SYS_RTCREAD);
+	    curr_eon++; 
+	}
+
+#ifdef RTC_DEBUG
+	if (sometimes == 1 ) 
+	    printk(KERN_INFO "RTC: process %i (%s) has woken at periodic tick %lu.\n", 
+	        current->pid, current->comm, curr_ptick);
+#endif
+
+	if (curr_eon == old_eon) {
+	    if (old_pcnt>curr_ptick) {
+	        result = 0;
+		printk(KERN_WARNING "RTC: clock is decrementing, returning 0x0 as value.\n");
+	    } else {
+	        result = (curr_ptick-old_pcnt) << 8;
+	    }
+	} else if (curr_eon == old_eon+1) {
+	    if (old_pcnt-curr_ptick>wrap_ptick) {
+	        result = 0;
+		printk(KERN_WARNING "RTC: clock is decrementing, returning 0x0 as value.\n");
+	    } else {
+	        result = (curr_ptick+(wrap_ptick-old_pcnt)) << 8;
+	    }
+	} else {
+	    result = 0xFFFFFFFF;
+	    printk(KERN_WARNING "RTC: eon has jumped, returning 0xFFFFFFFF as value.\n");
+	}
+
+	if (stab == 0) {
+	    result = 0xFFFFFFFF;
+	    printk(KERN_WARNING "RTC: first rtc_read, returning 0xFFFFFFFF as value.\n");
+	    stab = 1;
+	}
+
+#ifdef RTC_DEBUG
+	if (result == 0xFFFFFFFF) {
+	    tick_stat[255]++;
+	} else if ((result>>8) > 253) {
+	    tick_stat[254]++;
+	} else {
+	    tick_stat[result>>8]++;
+	} 
+
+	if (sometimes == 1 ) 
+	    printk(KERN_INFO "RTC: delta periodic ticks: %01u.\n", result>>8);
+#endif
+	old_pcnt = curr_ptick;
+	old_eon = curr_eon;
+    }
+//-----------------------------------------------------------------------------
+    else /* got_tick_irq == 1, fallback to fixed f=125 Hz */
+    {
+	curr_tick=readl(SYS_TOYREAD);
+#ifdef RTC_DEBUG
+	if (sometimes == 1 ) 
+	    printk(KERN_INFO "RTC: process %i (%s) is going to sleep at tick %lu.\n", 
+	        current->pid, current->comm, curr_tick);
+	sleep_diff=curr_tick-old_cnt;
+#endif
+//    wait_event_interruptible(period_q, (new_cnt>old_cnt));
+//    wait_event_interruptible(period_q, ((new_year>old_year) || (new_cnt>old_cnt)));
+//    wait_event_interruptible(period_q, ((new_year>old_year) || (new_cnt>old_cnt) || (stab==0)));
+	wait_event_interruptible(period_q, ((new_year!=old_year) || (new_cnt!=old_cnt) || (stab==0)));
+
+	curr_year=readl(SYS_TOYMATCH2);
+	curr_tick=readl(SYS_TOYREAD);
+        /* Check that new year did not occur right now, if so reread */
+	if ((curr_year == 1 + readl(SYS_TOYREAD))) {
+	    curr_tick=readl(SYS_TOYREAD);
+	    curr_year++; 
+	}
+
+#ifdef RTC_DEBUG
+	if (sometimes == 1 ) 
+	    printk(KERN_INFO "RTC: process %i (%s) has woken at tick %lu.\n", 
+	        current->pid, current->comm, curr_tick);
+#endif
+
+	trim = readl(SYS_TOYTRIM); /* Divide=sys_toytrim+1, max 16 bits */
+	trim &= 0xFFFF;
+	trim++; 
+    
+	if (curr_year == old_year) {
+	    if (old_cnt>curr_tick) {
+	        result = 0;
+		printk(KERN_WARNING "RTC: clock is decrementing, returning 0x0 as value.\n");
+	    } else {
+	        result = (curr_tick-old_cnt) << 8;
+	    }
+	} else if (curr_year == old_year+1) {
+	    leap_yr = ((!(old_year % 4) && (old_year % 100)) || !(old_year % 400));
+	    wrap_tick = yearly_seconds_normal + leap_yr * addl_seconds_leap;
+	    wrap_tick = wrap_tick * (freq / trim); /* ticks last year */
+	    if (old_cnt>curr_tick+wrap_tick) {
+	        result = 0;
+		printk(KERN_WARNING "RTC: clock is decrementing, returning 0x0 as value.\n");
+	    } else {
+	        result = (curr_tick+wrap_tick-old_cnt) << 8;
+	    }
+	} else {
+	    result = 0xFFFFFFFF;
+	    printk(KERN_WARNING "RTC: year has jumped, returning 0xFFFFFFFF as value.\n");
+	}
+
+	if (stab == 0) {
+	    result = 0xFFFFFFFF;
+	    printk(KERN_WARNING "RTC: first rtc_read, returning 0xFFFFFFFF as value.\n");
+	    stab = 1;
+	}
+
+#ifdef RTC_DEBUG
+	if (result == 0xFFFFFFFF) {
+	    tick_stat[255]++;
+	} else if ((result>>8) > 253) {
+	    tick_stat[254]++;
+	} else {
+	    tick_stat[result>>8]++;
+	} 
+
+	if (sometimes == 1 ) 
+	    printk(KERN_INFO "RTC: delta ticks: %01u.\n", result>>8);
+#endif
+	old_cnt = curr_tick;
+	old_year = curr_year;
+    }
+#ifdef RTC_DEBUG
+    if (sometimes == 1 ) 
+        printk(KERN_INFO DRIVER RTC_VERSION ", read finished.\n");
 #endif
+    return put_user(result, (size_t *)buf);
+} /* rtc_read */
 
-	(void) init_sysctl();
 
-	printk(KERN_INFO "Real Time Clock Driver v" RTC_VERSION "\n");
+/* File operations supported. */
 
-	return 0;
-}
+static struct file_operations rtc_fops = {
+    owner: THIS_MODULE,
+    llseek: no_llseek,
+    read: rtc_read,
+    ioctl: rtc_ioctl,
+    open: rtc_open,
+    release: rtc_release,
+};
 
-static void __exit rtc_exit (void)
-{
-	cleanup_sysctl();
-	remove_proc_entry ("driver/rtc", NULL);
-	misc_deregister(&rtc_dev);
-
-#ifdef __sparc__
-	if (rtc_has_irq)
-		free_irq (rtc_irq, &rtc_port);
-#else
-	if (RTC_IOMAPPED)
-		release_region(RTC_PORT(0), RTC_IO_EXTENT);
-	else
-		release_mem_region(RTC_PORT(0), RTC_IO_EXTENT);
-#if RTC_IRQ
-	if (rtc_has_irq)
-		free_irq (RTC_IRQ, NULL);
-#endif
-#endif /* __sparc__ */
-}
 
-module_init(rtc_init);
-module_exit(rtc_exit);
-EXPORT_NO_SYMBOLS;
+static struct miscdevice rtc_dev = {
+    RTC_MINOR,
+    "rtc",
+    &rtc_fops
+};
 
-#if RTC_IRQ
-/*
- * 	At IRQ rates >= 4096Hz, an interrupt may get lost altogether.
- *	(usually during an IDE disk interrupt, with IRQ unmasking off)
- *	Since the interrupt handler doesn't get called, the IRQ status
- *	byte doesn't get read, and the RTC stops generating interrupts.
- *	A timer is set, and will call this function if/when that happens.
- *	To get it out of this stalled state, we just read the status.
- *	At least a jiffy of interrupts (rtc_freq/HZ) will have been lost.
- *	(You *really* shouldn't be trying to use a non-realtime system 
- *	for something that requires a steady > 1KHz signal anyways.)
- */
 
-static void rtc_dropped_irq(unsigned long data)
+static int __init rtc_init(void)
 {
-	unsigned long freq;
-
-	spin_lock_irq (&rtc_lock);
+    unsigned long cntrctrl;
+    unsigned long rtc_init_flags; /* flags must not be passed to other functions */
+    int retcode = 0;
+
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", initialization started.\n");
+#endif
+
+    misc_register(&rtc_dev);
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", dev registered.\n");
+#endif
+    create_proc_read_entry("driver/rtc", 0, 0, rtc_read_proc, NULL);
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", proc created.\n");
+#endif
+
+    spin_lock_irqsave(&rtc_init_lock, rtc_init_flags);
+
+    /* common TOY & RTC set-up */
+    writel(0xFFFFFFFF, SYS_PININPUTEN); /* Enable count during sleep */
+    /* sys_cntrctrl has only two write-bits: bypass, BP, & enable osc, EO */
+    writel(0x00004000, SYS_COUNTER_CNTRL); /* Select GPIO[1], disable XTAL clk */
+
+    /* TOY set-up */
+    /* Set divide default once, not for every open/close */
+    cntrctrl=readl(SYS_COUNTER_CNTRL);
+    if (cntrctrl & SYS_CNTRCTRL_TTS)
+    { /* cannot write sys_toytrim */
+        /* wait or report error */
+	printk(KERN_WARNING "RTC: problem setting TOY trim.\n");
+    }
+    writel(trim_default, SYS_TOYTRIM); /* Divide=trim+1, max 16 bits */
+    /* leave TOY value as is, set with RTC_SET_TIME */
+    cntrctrl=readl(SYS_COUNTER_CNTRL);
+    if (cntrctrl & SYS_CNTRCTRL_TM0)
+    { /* cannot write sys_toymatch0 */
+        /* wait or report error */
+	printk(KERN_WARNING "RTC: problem setting yearly tick.\n");
+    }
+    writel(yearly_ticks_default, SYS_TOYMATCH0); /* set wrap-around value irq */
+
+    /* RTC set-up */
+    /* Set divide default once, not for every open/close */
+    cntrctrl=readl(SYS_COUNTER_CNTRL);
+    if (cntrctrl & SYS_CNTRCTRL_RTS)
+    { /* cannot write sys_rtctrim */
+        /* wait or report error */
+	printk(KERN_WARNING "RTC: problem setting RTC trim.\n");
+    }
+    writel(trim_default, SYS_RTCTRIM); /* Divide=trim+1, max 16 bits */
+    /* leave RTC value as is, does not matter */
+    cntrctrl=readl(SYS_COUNTER_CNTRL);
+    if (cntrctrl & SYS_CNTRCTRL_RM0)
+    { /* cannot write sys_rtcmatch0 */
+        /* wait or report error */
+	printk(KERN_WARNING "RTC: problem setting RTC wrap tick.\n");
+    }
+    writel(0xFFFFFFFF, SYS_RTCMATCH0); /* set wrap-around value irq */
+
+//-----------------------------------------------------------------------------
+    /* interrupt set-up, yearly irq */
+    writel(0x00008000, IC0_SRCSET);    /* toy match 0, not test bit */
+    writel(0x00008000, IC0_CFG0SET);   /* toy match 0, rising edge irq */
+    writel(0x00008000, IC0_CFG1CLR);   /* AMD 30283C page 198 */
+    writel(0x00008000, IC0_CFG2CLR);
+    writel(0x00008000, IC0_ASSIGNSET); /* toy match 0, select CPU req 0 */
+    writel(0x00008000, IC0_WAKESET);   /* toy match 0, wakeup from idle on match */
+    writel(0x00008000, IC0_RISINGCLR); /* toy match 0, clr rising edge detect */
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", requesting irq 15.\n");
+#endif
+    if (request_irq(15, year_elapsed_handler, SA_INTERRUPT, "/dev/rtc - year", TOYMATCH0_PTR))
+    /* (void *)&rtc_port */
+    {
+	printk(KERN_WARNING "RTC: cannot register IRQ %d. Yearly tick will be lost.\n", 15);
+//	spin_unlock_irqrestore(&rtc_init_lock, rtc_init_flags);
+//	return -EIO;
+	retcode = -EIO;
+    } else {
+#ifdef RTC_DEBUG
+	printk(KERN_INFO "RTC: got IRQ %d. Yearly tick interrupt available.\n", 15);
+#endif
+    }
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", enabling irq 15.\n");
+#endif
+    writel(0x00008000, IC0_MASKSET);   /* toy match 0, enable irq in PIC */
+//-----------------------------------------------------------------------------
+    /* interrupt set-up, eon irq */
+    writel(0x00080000, IC0_SRCSET);    /* rtc match 0, not test bit */
+    writel(0x00080000, IC0_CFG0SET);   /* rtc match 0, rising edge irq */
+    writel(0x00080000, IC0_CFG1CLR);   /* AMD 30283C page 198 */
+    writel(0x00080000, IC0_CFG2CLR);
+    writel(0x00080000, IC0_ASSIGNSET); /* rtc match 0, select CPU req 0 */
+    writel(0x00080000, IC0_WAKESET);   /* rtc match 0, wakeup from idle on match */
+    writel(0x00080000, IC0_RISINGCLR); /* rtc match 0, clr rising edge detect */
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", requesting irq 19.\n");
+#endif
+    if (request_irq(19, eon_elapsed_handler, SA_INTERRUPT, "/dev/rtc - wrap", RTCMATCH0_PTR))
+    /* (void *)&rtc_port */
+    {
+	printk(KERN_WARNING "RTC: cannot register IRQ %d. Periodic wrap-around will be lost.\n", 19);
+//	spin_unlock_irqrestore(&rtc_init_lock, rtc_init_flags);
+//	return -EIO;
+	retcode = -EIO;
+    } else {
+#ifdef RTC_DEBUG
+	printk(KERN_INFO "RTC: got IRQ %d. Periodic wrap-around interrupt available.\n", 19);
+#endif
+    }
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", enabling irq 19.\n");
+#endif
+    writel(0x00080000, IC0_MASKSET);   /* rtc match 0, enable irq in PIC */
+//-----------------------------------------------------------------------------
+    /* interrupt set-up, toy counter tick */
+    writel(0x00004000, IC0_SRCSET);    /* toy tick, not test bit */
+    writel(0x00004000, IC0_CFG0SET);   /* toy tick, rising edge irq */
+    writel(0x00004000, IC0_CFG1CLR);   /* AMD 30283C page 198 */
+    writel(0x00004000, IC0_CFG2CLR);
+    writel(0x00004000, IC0_ASSIGNSET); /* toy tick, select CPU req 0 */
+    writel(0x00004000, IC0_WAKESET);   /* toy tick, wakeup from idle on tick */
+    writel(0x00004000, IC0_RISINGCLR); /* toy tick, clr rising edge detect */
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", requesting irq 14.\n");
+#endif
+    if (request_irq(14, toy_tick_handler, SA_INTERRUPT, "/dev/rtc - tick", TOYTICK_PTR))
+    /* (void *)&rtc_port */
+    {
+	printk(KERN_WARNING "RTC: cannot register IRQ %d. Tick interrupt unavailable.\n", 14);
+//	spin_unlock_irqrestore(&rtc_init_lock, rtc_init_flags);
+//	return -EIO;
+	retcode = -EIO;
+    } else {
+#ifdef RTC_DEBUG
+	printk(KERN_INFO "RTC: got IRQ %d. Tick interrupt available.\n", 14);
+#endif
+        got_tick_irq = 1;
+    }
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", disabling irq 14.\n");
+#endif
+    /* irq 14 enabled by ioctl command RTC_UIE_ON */
+    //writel(0x00004000, IC0_MASKSET);   /* toy tick, enable irq in PIC */
+    writel(0x00004000, IC0_MASKCLR);   /* toy tick, disable irq in PIC */
+//-----------------------------------------------------------------------------
+    /* interrupt set-up, rtc counter tick */
+    writel(0x00040000, IC0_SRCSET);    /* rtc tick, not test bit */
+    writel(0x00040000, IC0_CFG0SET);   /* rtc tick, rising edge irq */
+    writel(0x00040000, IC0_CFG1CLR);   /* AMD 30283C page 198 */
+    writel(0x00040000, IC0_CFG2CLR);
+    writel(0x00040000, IC0_ASSIGNSET); /* rtc tick, select CPU req 0 */
+    writel(0x00040000, IC0_WAKESET);   /* rtc tick, wakeup from idle on tick */
+    writel(0x00040000, IC0_RISINGCLR); /* rtc tick, clr rising edge detect */
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", requesting irq 18.\n");
+#endif
+//    if (1) /* Test fallback */
+    if (request_irq(18, rtc_tick_handler, SA_INTERRUPT, "/dev/rtc - period", RTCTICK_PTR))
+    /* (void *)&rtc_port */
+    {
+	printk(KERN_WARNING "RTC: cannot register IRQ %d. Periodic interrupt unavailable.\n", 18);
+//	spin_unlock_irqrestore(&rtc_init_lock, rtc_init_flags);
+//	return -EIO; /* or fallback to irq 14 */
+	retcode = -EIO;
+    } else {
+#ifdef RTC_DEBUG
+	printk(KERN_INFO "RTC: got IRQ %d. Periodic interrupt available.\n", 18);
+#endif
+        got_period_irq = 1;
+    }
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", disabling irq 18.\n");
+#endif
+    /* irq 18 enabled by ioctl command RTC_PIE_ON */
+    //writel(0x00040000, IC0_MASKSET);   /* rtc tick, enable irq in PIC */
+    writel(0x00040000, IC0_MASKCLR); /* rtc tick, disable irq in PIC */
+
+    spin_unlock_irqrestore(&rtc_init_lock, rtc_init_flags);
 
-	/* Just in case someone disabled the timer from behind our back... */
-	if (rtc_status & RTC_TIMER_ON)
-		mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);
+    printk(KERN_INFO DRIVER RTC_VERSION ", initialized.\n");
 
-	rtc_irq_data += ((rtc_freq/HZ)<<8);
-	rtc_irq_data &= ~0xff;
-	rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);	/* restart */
+    return retcode;
+} /* rtc_init */
 
-	freq = rtc_freq;
 
-	spin_unlock_irq(&rtc_lock);
-
-	printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", freq);
-
-	/* Now we have new data */
-	wake_up_interruptible(&rtc_wait);
+static void __exit rtc_exit(void)
+{
+    unsigned long rtc_exit_flags; /* flags must not be passed to other functions */
 
-	kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
-}
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", exit started.\n");
 #endif
 
-/*
- *	Info exported via "/proc/driver/rtc".
- */
-
-static int rtc_proc_output (char *buf)
-{
-#define YN(bit) ((ctrl & bit) ? "yes" : "no")
-#define NY(bit) ((ctrl & bit) ? "no" : "yes")
-	char *p;
-	struct rtc_time tm;
-	unsigned char batt, ctrl;
-	unsigned long freq;
-
-	spin_lock_irq(&rtc_lock);
-	batt = CMOS_READ(RTC_VALID) & RTC_VRT;
-	ctrl = CMOS_READ(RTC_CONTROL);
-	freq = rtc_freq;
-	spin_unlock_irq(&rtc_lock);
-
-	p = buf;
-
-	get_rtc_time(&tm);
-
-	/*
-	 * There is no way to tell if the luser has the RTC set for local
-	 * time or for Universal Standard Time (GMT). Probably local though.
-	 */
-	p += sprintf(p,
-		     "rtc_time\t: %02d:%02d:%02d\n"
-		     "rtc_date\t: %04d-%02d-%02d\n"
-	 	     "rtc_epoch\t: %04lu\n",
-		     tm.tm_hour, tm.tm_min, tm.tm_sec,
-		     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
-
-	get_rtc_alm_time(&tm);
-
-	/*
-	 * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will
-	 * match any value for that particular field. Values that are
-	 * greater than a valid time, but less than 0xc0 shouldn't appear.
-	 */
-	p += sprintf(p, "alarm\t\t: ");
-	if (tm.tm_hour <= 24)
-		p += sprintf(p, "%02d:", tm.tm_hour);
-	else
-		p += sprintf(p, "**:");
-
-	if (tm.tm_min <= 59)
-		p += sprintf(p, "%02d:", tm.tm_min);
-	else
-		p += sprintf(p, "**:");
-
-	if (tm.tm_sec <= 59)
-		p += sprintf(p, "%02d\n", tm.tm_sec);
-	else
-		p += sprintf(p, "**\n");
-
-	p += sprintf(p,
-		     "DST_enable\t: %s\n"
-		     "BCD\t\t: %s\n"
-		     "24hr\t\t: %s\n"
-		     "square_wave\t: %s\n"
-		     "alarm_IRQ\t: %s\n"
-		     "update_IRQ\t: %s\n"
-		     "periodic_IRQ\t: %s\n"
-		     "periodic_freq\t: %ld\n"
-		     "batt_status\t: %s\n",
-		     YN(RTC_DST_EN),
-		     NY(RTC_DM_BINARY),
-		     YN(RTC_24H),
-		     YN(RTC_SQWE),
-		     YN(RTC_AIE),
-		     YN(RTC_UIE),
-		     YN(RTC_PIE),
-		     freq,
-		     batt ? "okay" : "dead");
-
-	return  p - buf;
-#undef YN
-#undef NY
-}
-
-static int rtc_read_proc(char *page, char **start, off_t off,
-                         int count, int *eof, void *data)
-{
-        int len = rtc_proc_output (page);
-        if (len <= off+count) *eof = 1;
-        *start = page + off;
-        len -= off;
-        if (len>count) len = count;
-        if (len<0) len = 0;
-        return len;
-}
+    spin_lock_irqsave(&rtc_init_lock, rtc_exit_flags);
 
-/*
- * Returns true if a clock update is in progress
- */
-/* FIXME shouldn't this be above rtc_init to make it fully inlined? */
-static inline unsigned char rtc_is_updating(void)
-{
-	unsigned char uip;
+    writel(0x00080000, IC0_MASKCLR); /* rtc match 0, disable irq in PIC */
+    free_irq(19, RTCMATCH0_PTR);     /* rtc match 0, disable irq in CP0 reg 12, sel 0 */
+    writel(0x00008000, IC0_MASKCLR); /* toy match 0, disable irq in PIC */
+    free_irq(15, TOYMATCH0_PTR);     /* toy match 0, disable irq in CP0 reg 12, sel 0 */
 
-	spin_lock_irq(&rtc_lock);
-	uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
-	spin_unlock_irq(&rtc_lock);
-	return uip;
-}
+    if (got_tick_irq == 1) {
+        writel(0x00004000, IC0_MASKCLR); /* toy tick, disable irq in PIC */
+        free_irq(14, TOYTICK_PTR);       /* toy tick, disable irq in CP0 reg 12, sel 0 */
+        got_tick_irq = 0;
+    }
 
-static void get_rtc_time(struct rtc_time *rtc_tm)
-{
-	unsigned long uip_watchdog = jiffies;
-	unsigned char ctrl;
-#ifdef CONFIG_DECSTATION
-	unsigned int real_year;
-#endif
-
-	/*
-	 * read RTC once any update in progress is done. The update
-	 * can take just over 2ms. We wait 10 to 20ms. There is no need to
-	 * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
-	 * If you need to know *exactly* when a second has started, enable
-	 * periodic update complete interrupts, (via ioctl) and then 
-	 * immediately read /dev/rtc which will block until you get the IRQ.
-	 * Once the read clears, read the RTC time (again via ioctl). Easy.
-	 */
-
-	if (rtc_is_updating() != 0)
-		while (jiffies - uip_watchdog < 2*HZ/100) {
-			barrier();
-			cpu_relax();
-		}
-
-	/*
-	 * Only the values that we read from the RTC are set. We leave
-	 * tm_wday, tm_yday and tm_isdst untouched. Even though the
-	 * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
-	 * by the RTC when initially set to a non-zero value.
-	 */
-	spin_lock_irq(&rtc_lock);
-	rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
-	rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
-	rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
-	rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
-	rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
-	rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
-#ifdef CONFIG_DECSTATION
-	real_year = CMOS_READ(RTC_DEC_YEAR);
-#endif
-	ctrl = CMOS_READ(RTC_CONTROL);
-	spin_unlock_irq(&rtc_lock);
-
-	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
-	{
-		BCD_TO_BIN(rtc_tm->tm_sec);
-		BCD_TO_BIN(rtc_tm->tm_min);
-		BCD_TO_BIN(rtc_tm->tm_hour);
-		BCD_TO_BIN(rtc_tm->tm_mday);
-		BCD_TO_BIN(rtc_tm->tm_mon);
-		BCD_TO_BIN(rtc_tm->tm_year);
-	}
+    if (got_period_irq == 1) {
+        writel(0x00040000, IC0_MASKCLR); /* rtc tick, disable irq in PIC */
+        free_irq(18, RTCTICK_PTR);       /* rtc tick, disable irq in CP0 reg 12, sel 0 */
+        got_period_irq = 0;
+    }
+    /* Counters will continue, but not generating interrupts */
 
-#ifdef CONFIG_DECSTATION
-	rtc_tm->tm_year += real_year - 72;
-#endif
+    spin_unlock_irqrestore(&rtc_init_lock, rtc_exit_flags);
 
-	/*
-	 * Account for differences between how the RTC uses the values
-	 * and how they are defined in a struct rtc_time;
-	 */
-	if ((rtc_tm->tm_year += (epoch - 1900)) <= 69)
-		rtc_tm->tm_year += 100;
+    remove_proc_entry("driver/rtc", NULL);
+    misc_deregister(&rtc_dev);
 
-	rtc_tm->tm_mon--;
-}
+    printk(KERN_INFO DRIVER RTC_VERSION ", exited.\n");
+} /* rtc_exit */
 
-static void get_rtc_alm_time(struct rtc_time *alm_tm)
-{
-	unsigned char ctrl;
+module_init(rtc_init);
+module_exit(rtc_exit);
+EXPORT_NO_SYMBOLS;
 
-	/*
-	 * Only the values that we read from the RTC are set. That
-	 * means only tm_hour, tm_min, and tm_sec.
-	 */
-	spin_lock_irq(&rtc_lock);
-	alm_tm->tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
-	alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM);
-	alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM);
-	ctrl = CMOS_READ(RTC_CONTROL);
-	spin_unlock_irq(&rtc_lock);
-
-	if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
-	{
-		BCD_TO_BIN(alm_tm->tm_sec);
-		BCD_TO_BIN(alm_tm->tm_min);
-		BCD_TO_BIN(alm_tm->tm_hour);
-	}
-}
 
-#if RTC_IRQ
-/*
- * Used to disable/enable interrupts for any one of UIE, AIE, PIE.
- * Rumour has it that if you frob the interrupt enable/disable
- * bits in RTC_CONTROL, you should read RTC_INTR_FLAGS, to
- * ensure you actually start getting interrupts. Probably for
- * compatibility with older/broken chipset RTC implementations.
- * We also clear out any old irq data after an ioctl() that
- * meddles with the interrupt enable/disable bits.
- */
+/* Info exported via "/proc/driver/rtc". */
 
-static void mask_rtc_irq_bit(unsigned char bit)
+static int rtc_proc_output(char *buf)
 {
-	unsigned char val;
+    char *p;
+    struct rtc_time rtc_tm;
+    unsigned long trim, rtctrim;
+    unsigned short corr;
+#ifdef RTC_DEBUG
+    unsigned char index;
+#endif
+
+    au1550_get_time(&rtc_tm);
+
+    trim = readl(SYS_TOYTRIM); /* Divide=sys_toytrim+1, max 16 bits */
+    trim &= 0xFFFF;
+    trim++; 
+
+    rtctrim = readl(SYS_RTCTRIM); /* Divide=sys_rtctrim+1, max 16 bits */
+    rtctrim &= 0xFFFF;
+    rtctrim++; 
+    corr=freq/rtctrim;
+
+    p = buf;
+    p += sprintf(p,
+	"rtc_time     \t: %02d:%02d:%02d\n"
+	"rtc_date     \t: %04d-%02d-%02d\n"
+	"rtc_epoch    \t: %04u\n"
+#ifdef RTC_DEBUG
+        "year_IRQ     \t: %01lu\n"
+        "eon_IRQ      \t: %01lu\n"
+        "spurious_IRQ \t: %01lu\n"
+        "write error  \t: %01lu\n"
+        "toy tick     \t: %01u\n"
+        "year tick    \t: %01u\n"
+        "year         \t: %04u\n"
+        "periodic tick\t: %01u\n"
+        "cp0_cause    \t: %08lx\n"
+        "cp0_status   \t: %08lx\n"
+//      "ic0_risingrd \t: %08lx\n"
+//      "ic0_fallingrd\t: %08lx\n"
+//      "ic0_req0int  \t: %08lx\n"
+        "sleep diff   \t: %01d\n"
+#endif
+	"update_IRQ \t: %s\n"
+	"update_freq\t: %01lu Hz\n"
+	"periodic_IRQ \t: %s\n"
+	"periodic_freq\t: %u.%03lu Hz\n",
+	rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec,
+	epoch+rtc_tm.tm_year, rtc_tm.tm_mon + 1, rtc_tm.tm_mday, 
+	epoch, 
+#ifdef RTC_DEBUG
+	year_irq,
+	eon_irq,
+	spurious_irq,
+        write_error,
+	readl(SYS_TOYREAD),
+        readl(SYS_TOYMATCH0),
+        readl(SYS_TOYMATCH2),
+	readl(SYS_RTCREAD),
+        cp0_cause,
+        cp0_status,
+//        ic0_risingrd,
+//        ic0_fallingrd,
+//	  ic0_req0int,
+	sleep_diff,
+#endif
+        (readl(IC0_MASKRD) & 0x00004000) ? "enabled" : "disabled",
+	freq/trim,
+        (readl(IC0_MASKRD) & 0x00040000) ? "enabled" : "disabled",
+	corr, ((freq-corr*rtctrim)*1000)/rtctrim
+        );
+
+#ifdef RTC_DEBUG
+    p += sprintf(p, "Time between read statistics:\n");
+    for (index = 0; index < 254; index++) {
+        if (tick_stat[index]>0)
+            p += sprintf(p, "%05d tick(s)  \t: %10lu\n", index, tick_stat[index]);
+    }
+    index=254;
+    p += sprintf(p, " >253 ticks   \t: %10lu\n", tick_stat[index]);
+    index=255;
+    p += sprintf(p, "error ticks   \t: %10lu\n", tick_stat[index]);
+#endif
+	
+    return (p - buf);
+} /* rtc_proc_output */
 
-	spin_lock_irq(&rtc_lock);
-	val = CMOS_READ(RTC_CONTROL);
-	val &=  ~bit;
-	CMOS_WRITE(val, RTC_CONTROL);
-	CMOS_READ(RTC_INTR_FLAGS);
-
-	rtc_irq_data = 0;
-	spin_unlock_irq(&rtc_lock);
-}
 
-static void set_rtc_irq_bit(unsigned char bit)
+static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
-	unsigned char val;
-
-	spin_lock_irq(&rtc_lock);
-	val = CMOS_READ(RTC_CONTROL);
-	val |= bit;
-	CMOS_WRITE(val, RTC_CONTROL);
-	CMOS_READ(RTC_INTR_FLAGS);
-
-	rtc_irq_data = 0;
-	spin_unlock_irq(&rtc_lock);
-}
+    int length = rtc_proc_output(page);
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", read proc started.\n");
+#endif
+    if (length <= off + count) *eof = 1;
+    *start = page + off;
+    length -= off;
+    if (length > count) length = count;
+    if (length < 0) length = 0;
+#ifdef RTC_DEBUG
+    printk(KERN_INFO DRIVER RTC_VERSION ", read proc finished.\n");
 #endif
+    return length;
+} /* rtc_read_proc */
 
-MODULE_AUTHOR("Paul Gortmaker");
+MODULE_AUTHOR("K. Monell");
+MODULE_LICENSE("Proprietary");
 MODULE_LICENSE("GPL");
diff -x asm -Naur linux-2.4.30-orig/drivers/i2c/i2c-algo-au1550.c linux-2.4.30/drivers/i2c/i2c-algo-au1550.c
--- linux-2.4.30-orig/drivers/i2c/i2c-algo-au1550.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/i2c/i2c-algo-au1550.c	2006-12-11 18:18:37.000000000 +0100
@@ -19,6 +19,8 @@
 #include <linux/i2c.h>
 #include <linux/i2c-algo-au1550.h>
 
+static void au1550_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
 static int
 wait_xfer_done(struct i2c_algo_au1550_data *adap)
 {
@@ -28,6 +30,8 @@
 
 	sp = (volatile psc_smb_t *)(adap->psc_base);
 
+//	printk("wait_xfer_done: sleeping %d us\n", adap->xfer_timeout);
+
 	/* Wait for Tx FIFO Underflow.
 	*/
 	for (i = 0; i < adap->xfer_timeout; i++) {
@@ -42,6 +46,8 @@
 		udelay(1);
 	}
 
+//	printk("wait_xfer_done: timed out\n");
+
 	return -ETIMEDOUT;
 }
 
@@ -95,23 +101,21 @@
 
 	sp = (volatile psc_smb_t *)(adap->psc_base);
 
+//	printk("do_address(): 0x%p, 0x%02x\n", sp, addr & 0xff);
+
 	/* Reset the FIFOs, clear events.
 	*/
-	stat = sp->psc_smbstat;
+	sp->psc_smbpcr = PSC_SMBPCR_DC;
 	sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
 	au_sync();
-    
-	if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE))
-	{
-		
-		sp->psc_smbpcr = PSC_SMBPCR_DC;
-		au_sync();
 	do {
 		stat = sp->psc_smbpcr;
 		au_sync();
+//		printk("do_address(): in loop\n");
 	} while ((stat & PSC_SMBPCR_DC) != 0);
-		udelay(50);
-	}
+
+//	printk("do_address(): out of loop\n");
+
 	/* Write out the i2c chip address and specify operation
 	*/
 	addr <<= 1;
@@ -126,6 +130,7 @@
 	au_sync();
 	if (wait_ack(adap))
 		return -EIO;
+
 	return 0;
 }
 
@@ -262,6 +267,105 @@
 	return err;
 }
 
+
+static int
+au1550_slave_recv(struct i2c_adapter *adapter, char * buf, int num)
+{
+    int count;
+
+    for(count=0 ; count < num ; count++) {
+	if(i2c_buffer_read(&adapter->readbuf, &buf[count]) < 0)
+	    break;
+    }
+
+//    printk("au1550_slave_recv(): count = %d\n", count);
+
+    return count;
+}
+
+
+static int
+au1550_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned long arg)
+{
+	struct i2c_algo_au1550_data *adap = adapter->algo_data;
+	volatile psc_smb_t	*sp = (volatile psc_smb_t *)(adap->psc_base);
+	int retval;
+
+        printk(KERN_DEBUG "au1550_control(0x%08x, 0x%08x, 0x%08x)\n",
+	       (unsigned int)adapter, cmd, (unsigned int)arg);
+
+	switch(cmd) {
+	case I2C_SET_SLAVE_MODE: {
+	    if((arg > 0xfe) || (arg < 1)) {
+		printk(KERN_ERR "i2c-algo-au1550.o: Invalid device address for slave mode (0x%08x\n",
+		       (unsigned int)arg);
+		return -EINVAL;
+	    }
+
+	    if(adapter->mode == I2C_SLAVE_MODE) {
+		printk(KERN_DEBUG "i2c-algo-au1550: adapter already configured as slave, ignoring request\n");
+
+		return 0;
+	    }
+
+	    printk(KERN_DEBUG "i2c-algo-au1550.o: Setting slave mode - (0x%08x)\n",
+		   (unsigned int)arg);
+
+	    /* Enable interrupts */
+	    retval = request_irq(adap->psc_irq, au1550_interrupt, 0, DRIVER_NAME, adapter);
+	    printk(KERN_DEBUG "request_irq retval == %d\n", retval);
+
+	    /* Disable and configure PSC */
+	    sp->psc_smbcfg = (sp->psc_smbcfg & ~PSC_SMBCFG_DE_ENABLE);
+	    au_sync();
+	    sp->psc_smbcfg = ((sp->psc_smbcfg | PSC_SMBCFG_GCE | (arg << 1)) & 0x3fffffff);
+	    sp->psc_smbmsk = PSC_SMBMSK_ALLMASK & ~((7 <<11) | 0x20);
+	    au_sync();
+
+	    /* Enable PSC */
+	    sp->psc_smbcfg = sp->psc_smbcfg | PSC_SMBCFG_DE_ENABLE;
+	    au_sync();
+
+	    adapter->mode = I2C_SLAVE_MODE;
+	} break;
+	case I2C_SET_MASTER_MODE: {
+#if 0
+	    {
+		volatile u32 * gpioclear = 0xb190010clu;
+		volatile u32 * gpioset = 0xb1900108lu;
+	    
+		*gpioclear = 0x00008000;
+		*gpioset = 0x00008000;
+	    }
+#endif
+	    if(adapter->mode == I2C_MASTER_MODE) {
+		printk(KERN_DEBUG "i2c-algo-au1550: adapter already configured as master, ignoring request\n");
+		return 0;
+	    }
+
+	    printk(KERN_DEBUG "i2c-algo-au1550.o: Setting master mode\n");
+
+	    /* Configure PSC */
+	    sp->psc_smbmsk = PSC_SMBMSK_ALLMASK;
+	    sp->psc_smbcfg = (sp->psc_smbcfg & ~PSC_SMBCFG_DE_ENABLE);
+	    sp->psc_smbcfg = sp->psc_smbcfg & ~(PSC_SMBCFG_GCE | (0x7f << 1));
+
+	    /* Enable PSC */
+	    sp->psc_smbcfg = sp->psc_smbcfg | PSC_SMBCFG_DE_ENABLE;
+	    au_sync();
+
+	    /* Disable interrupts */
+	    free_irq(adap->psc_irq, adapter);
+
+	    adapter->mode = I2C_MASTER_MODE;
+	} break;
+	default:
+	    printk("huh?\n");
+	}
+
+        return 0;
+}
+
 static u32
 au1550_func(struct i2c_adapter *adap)
 {
@@ -272,6 +376,8 @@
 	.name		= "Au1550 algorithm",
 	.id		= I2C_ALGO_AU1550,
 	.master_xfer	= au1550_xfer,
+	.slave_recv     = au1550_slave_recv,
+	.algo_control   = au1550_control,
 	.functionality	= au1550_func,
 };
 
@@ -304,23 +410,23 @@
 		au_sync();
 	} while ((stat & PSC_SMBSTAT_SR) == 0);
 
-	sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO1 | PSC_SMBCFG_TT_FIFO1 |
-				PSC_SMBCFG_DD_DISABLE | PSC_SMBCFG_SFM);
+	sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 |
+				PSC_SMBCFG_DD_DISABLE);
 
 	/* Divide by 8 to get a 6.25 MHz clock.  The later protocol
 	 * timings are based on this clock.
 	 */
-	sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV2);
+	sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
 	sp->psc_smbmsk = PSC_SMBMSK_ALLMASK;
 	au_sync();
 
 	/* Set the protocol timer values.  See Table 71 in the
 	 * Au1550 Data Book for standard timing values.
 	 */
-	sp->psc_smbtmr = PSC_SMBTMR_SET_TH(2) | PSC_SMBTMR_SET_PS(15) | \
-		PSC_SMBTMR_SET_PU(11) | PSC_SMBTMR_SET_SH(11) | \
-		PSC_SMBTMR_SET_SU(11) | PSC_SMBTMR_SET_CL(15) | \
-		PSC_SMBTMR_SET_CH(11);
+	sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
+		PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \
+		PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \
+		PSC_SMBTMR_SET_CH(15);
 	au_sync();
 
 	sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE;
@@ -345,3 +451,49 @@
 MODULE_AUTHOR("Dan Malek <dan@embeddededge.com>");
 MODULE_DESCRIPTION("SMBus Au1550 algorithm");
 MODULE_LICENSE("GPL");
+
+
+static void au1550_interrupt(int irq, void * adapter, struct pt_regs *regs)
+{
+    struct i2c_adapter *foobar = (struct i2c_adapter *) adapter;
+    struct i2c_algo_au1550_data *adap = foobar->algo_data;
+    volatile psc_smb_t *sp = (volatile psc_smb_t *)(adap->psc_base);
+    u32 reg;
+    //    u32 event;
+    u8 byte;
+
+//    printk("i2c-au1550: interrupt! Event: 0x%08x\n", event);
+
+    spin_lock(&foobar->intlock);
+
+    while((sp->psc_smbstat & PSC_SMBSTAT_RE) == 0) {
+	reg = sp->psc_smbtxrx;
+	byte = reg & 0xff;
+
+	i2c_buffer_write(&(foobar->readbuf), &byte);
+    }
+	
+    /* Clear event flag */
+    sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
+
+    spin_unlock(&foobar->intlock);
+
+    /* Notify waiting processes */
+    wake_up_interruptible(&foobar->readq);
+
+#if 0
+    struct i2c_adapter *foobar = (struct i2c_adapter *) adapter;
+    struct i2c_algo_au1550_data *adap = foobar->algo_data;
+    volatile psc_smb_t *sp = (volatile psc_smb_t *)(adap->psc_base);
+    u32 foo;
+
+    while((sp->psc_smbstat & (1<<12)) == 0) {
+        foo = sp->psc_smbtxrx;
+        printk("0x%02x ", foo & 0xff);
+    }
+
+    printk("i2c-au1550: interrupt! Event: 0x%08x\n", sp->psc_smbevnt);
+
+    sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
+#endif
+}
diff -x asm -Naur linux-2.4.30-orig/drivers/i2c/i2c-au1550.c linux-2.4.30/drivers/i2c/i2c-au1550.c
--- linux-2.4.30-orig/drivers/i2c/i2c-au1550.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/i2c/i2c-au1550.c	2006-12-11 18:18:37.000000000 +0100
@@ -29,27 +29,11 @@
 
 #include <asm/au1000.h>
 #include <asm/au1xxx_psc.h>
-#if defined( CONFIG_MIPS_PB1550 )
-	#include <asm/pb1550.h>
-#endif
-#if defined( CONFIG_MIPS_DB1550 )
-	#include <asm/db1x00.h>
-#endif
-#if defined( CONFIG_MIPS_PB1200 )
-	#include <asm/pb1200.h>
-#endif
-#if defined( CONFIG_MIPS_DB1200 )
-	#include <asm/db1200.h>
-#endif
-#if defined( CONFIG_MIPS_FICMMP )
-	#include <asm/ficmmp.h>
-#endif
+#include <asm/mood300.h>
 
 #include <linux/i2c.h>
 #include <linux/i2c-algo-au1550.h>
 
-
-
 static int
 pb1550_reg(struct i2c_client *client)
 {
@@ -79,11 +63,11 @@
 }
 
 static struct i2c_algo_au1550_data pb1550_i2c_info = {
-	SMBUS_PSC_BASE, 200, 200
+        SMBUS_PSC_BASE, AU1550_PSC2_INT, 200, 200
 };
 
 static struct i2c_adapter pb1550_board_adapter = {
-	name:              "pb1550 adapter",
+	name:              "pb1550 adapter (PSC2)",
 	id:                I2C_HW_AU1550_PSC,
 	algo:              NULL,
 	algo_data:         &pb1550_i2c_info,
@@ -94,9 +78,26 @@
 	client_count:      0,
 };
 
+static struct i2c_algo_au1550_data pb1550_i2c_info2 = {
+        PSC3_BASE_ADDR, AU1550_PSC3_INT, 200, 200
+};
+
+static struct i2c_adapter pb1550_board_adapter2 = {
+	name:              "pb1550 adapter (PSC3)",
+	id:                I2C_HW_AU1550_PSC,
+	algo:              NULL,
+	algo_data:         &pb1550_i2c_info2,
+	inc_use:           pb1550_inc_use,
+	dec_use:           pb1550_dec_use,
+	client_register:   pb1550_reg,
+	client_unregister: pb1550_unreg,
+	client_count:      0,
+};
+
 int __init
 i2c_pb1550_init(void)
 {
+
 	/* This is where we would set up a 50MHz clock source
 	 * and routing.  On the Pb1550, the SMBus is PSC2, which
 	 * uses a shared clock with USB.  This has been already
@@ -106,39 +107,12 @@
         if (i2c_au1550_add_bus(&pb1550_board_adapter) < 0)
                 return -ENODEV;
 
-	return 0;
-}
-
-/* BIG hack to support the control interface on the Wolfson WM8731
- * audio codec on the Pb1550 board.  We get an address and two data
- * bytes to write, create an i2c message, and send it across the
- * i2c transfer function.  We do this here because we have access to
- * the i2c adapter structure.
- */
-static struct i2c_msg wm_i2c_msg;  /* We don't want this stuff on the stack */
-static	u8 i2cbuf[2];
-
-int
-pb1550_wm_codec_write(u8 addr, u8 reg, u8 val)
-{
-	wm_i2c_msg.addr = addr;
-	wm_i2c_msg.flags = 0;
-	wm_i2c_msg.buf = i2cbuf;
-	wm_i2c_msg.len = 2;
-	i2cbuf[0] = reg;
-	i2cbuf[1] = val;
-
-	return pb1550_board_adapter.algo->master_xfer(&pb1550_board_adapter, &wm_i2c_msg, 1);
-}
+        if (i2c_au1550_add_bus(&pb1550_board_adapter2) < 0)
+                return -ENODEV;
 
-/* the next function is needed by DVB driver. */
-int pb1550_i2c_xfer(struct i2c_msg msgs[], int num)
-{
-    return pb1550_board_adapter.algo->master_xfer(&pb1550_board_adapter, msgs, num);
+	return 0;
 }
 
-EXPORT_SYMBOL(pb1550_wm_codec_write);
-EXPORT_SYMBOL(pb1550_i2c_xfer);
 
 MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC.");
 MODULE_DESCRIPTION("SMBus adapter Alchemy pb1550");
@@ -154,4 +128,5 @@
 cleanup_module(void)
 {
 	i2c_au1550_del_bus(&pb1550_board_adapter);
+	i2c_au1550_del_bus(&pb1550_board_adapter2);
 }
diff -x asm -Naur linux-2.4.30-orig/drivers/i2c/i2c-core.c linux-2.4.30/drivers/i2c/i2c-core.c
--- linux-2.4.30-orig/drivers/i2c/i2c-core.c	2005-05-12 17:57:22.000000000 +0200
+++ linux-2.4.30/drivers/i2c/i2c-core.c	2006-12-11 18:18:37.000000000 +0100
@@ -20,7 +20,7 @@
 /* With some changes from Ky�sti M�lkki <kmalkki@cc.hut.fi>.
    All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> */
 
-/* $Id: i2c-core.c,v 1.64 2001/08/13 01:35:56 mds Exp $ */
+/* $Id: i2c-core.c,v 1.3 2005/10/14 11:03:25 las Exp $ */
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -133,6 +133,14 @@
 	/* init data types */
 	init_MUTEX(&adap->lock);
 
+	adap->readbuf.data = kmalloc(1024, GFP_KERNEL);
+	adap->readbuf.buffsize = 1024;
+	adap->readbuf.readpos = adap->readbuf.writepos = 0;
+
+	init_waitqueue_head(&adap->readq);
+
+	adap->intlock = SPIN_LOCK_UNLOCKED;
+
 #ifdef CONFIG_PROC_FS
 
 	if (i2cproc_initialized) {
@@ -239,6 +247,8 @@
 	}
 #endif /* def CONFIG_PROC_FS */
 
+	kfree(adap->readbuf.data);
+
 	adapters[i] = NULL;
 	adap_count--;
 	
@@ -750,11 +760,14 @@
 		msg.addr   = client->addr;
 		msg.flags = client->flags & I2C_M_TEN;
 		msg.len = count;
-		(const char *)msg.buf = buf;
+		msg.buf = (__u8 *)buf;
 	
 		DEB2(printk(KERN_DEBUG "i2c-core.o: master_send: writing %d bytes on %s.\n",
 			count,client->adapter->name));
 	
+		//		printk("i2c-core.o: master_send: writing %d bytes on %s to addr: 0x%02x.\n",
+		//		       count,client->adapter->name, client->addr);
+	
 		I2C_LOCK(adap);
 		ret = adap->algo->master_xfer(adap,&msg,1);
 		I2C_UNLOCK(adap);
@@ -775,6 +788,9 @@
 	struct i2c_adapter *adap=client->adapter;
 	struct i2c_msg msg;
 	int ret;
+
+//	printk("i2c-core.o: i2c_master_recv()\n");
+
 	if (client->adapter->algo->master_xfer) {
 		msg.addr   = client->addr;
 		msg.flags = client->flags & I2C_M_TEN;
@@ -785,17 +801,22 @@
 		DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: reading %d bytes on %s.\n",
 			count,client->adapter->name));
 	
+//		printk("i2c-core.o: master_recv: reading %d bytes on %s.\n",
+//			count,client->adapter->name);
+	
 		I2C_LOCK(adap);
 		ret = adap->algo->master_xfer(adap,&msg,1);
 		I2C_UNLOCK(adap);
 	
 		DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n",
 			ret, count, client->addr));
+//		printk("i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n",
+//			ret, count, client->addr);
 	
 		/* if everything went ok (i.e. 1 msg transmitted), return #bytes
 	 	* transmitted, else error code.
 	 	*/
-		return (ret == 1 )? count : ret;
+		return (ret == 1 ) ? count : ret;
 	} else {
 		printk(KERN_ERR "i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n",
 		       client->adapter->id);
@@ -803,6 +824,35 @@
 	}
 }
 
+int i2c_slave_recv(struct i2c_adapter *adap, char *buf ,int count)
+{
+	int ret;
+
+	if (adap->algo->slave_recv) {
+		DEB2(printk(KERN_DEBUG "i2c-core.o: slave_recv: reading %d bytes on %s.\n",
+			count, adap->name));
+	
+		I2C_LOCK(adap);
+		ret = adap->algo->slave_recv(adap, buf, count);
+		I2C_UNLOCK(adap);
+	
+		DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d)\n",
+			ret, count));
+	
+		/* if everything went ok (i.e. 1 msg transmitted), return #bytes
+	 	* transmitted, else error code.
+	 	*/
+		
+//		printk("i2c_slave_recv(): ret = %d\n", ret);
+
+		return ret;
+	} else {
+		printk(KERN_ERR "i2c-core.o: I2C adapter %04x: I2C slave transfers not supported\n",
+		       adap->id);
+		return -ENOSYS;
+	}
+}
+
 
 int i2c_control(struct i2c_client *client,
 	unsigned int cmd, unsigned long arg)
@@ -901,7 +951,7 @@
 			if (addr == address_data->normal_i2c[i]) {
 				found = 1;
 				DEB2(printk(KERN_DEBUG "i2c-core.o: found normal i2c entry for adapter %d, "
-				            "addr %02x\n", adap_id, addr));
+				            "addr %02x", adap_id,addr));
 			}
 		}
 
@@ -963,6 +1013,37 @@
 	return -1;
 }
 
+
+int i2c_buffer_write(struct i2c_buffer *buffer, unsigned char *inByte)
+{
+//    printk("i2c_buffer_write(0x%02x): size = %d, read = %d, write = %d\n", *inByte & 0xff, buffer->buffsize, buffer->readpos, buffer->writepos);
+
+    if (((buffer->writepos + 1) % buffer->buffsize) != buffer->readpos) {
+	buffer->data[buffer->writepos] = *inByte;
+	buffer->writepos = (buffer->writepos + 1) % buffer->buffsize;
+	return 0;
+    }
+
+    return -1;
+}
+
+int i2c_buffer_read(struct i2c_buffer *buffer, unsigned char *outByte)
+{
+    if (buffer->readpos != buffer->writepos) {
+	*outByte = buffer->data[buffer->readpos];
+	buffer->readpos = (buffer->readpos + 1) % buffer->buffsize;
+
+//	printk("i2c_buffer_read(0x%02x): size = %d, read = %d, write = %d\n", *outByte & 0xff, buffer->buffsize, buffer->readpos, buffer->writepos);
+
+	return 0;
+    }
+
+//    printk("i2c_buffer_read(): buffer empty!\n");
+
+    return -1;
+}
+
+
 /* The SMBus parts */
 
 extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value)
@@ -1098,8 +1179,8 @@
 	  need to use only one message; when reading, we need two. We initialize
 	  most things with sane defaults, to keep the code below somewhat
 	  simpler. */
-	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+2];
-	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
+	unsigned char msgbuf0[34];
+	unsigned char msgbuf1[34];
 	int num = read_write == I2C_SMBUS_READ?2:1;
 	struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, 
 	                          { addr, flags | I2C_M_RD, 0, msgbuf1 }
@@ -1372,11 +1453,15 @@
 
 EXPORT_SYMBOL(i2c_master_send);
 EXPORT_SYMBOL(i2c_master_recv);
+EXPORT_SYMBOL(i2c_slave_recv);
 EXPORT_SYMBOL(i2c_control);
 EXPORT_SYMBOL(i2c_transfer);
 EXPORT_SYMBOL(i2c_adapter_id);
 EXPORT_SYMBOL(i2c_probe);
 
+EXPORT_SYMBOL(i2c_buffer_write);
+EXPORT_SYMBOL(i2c_buffer_read);
+
 EXPORT_SYMBOL(i2c_smbus_xfer);
 EXPORT_SYMBOL(i2c_smbus_write_quick);
 EXPORT_SYMBOL(i2c_smbus_read_byte);
diff -x asm -Naur linux-2.4.30-orig/drivers/i2c/i2c-dev.c linux-2.4.30/drivers/i2c/i2c-dev.c
--- linux-2.4.30-orig/drivers/i2c/i2c-dev.c	2005-05-12 17:57:22.000000000 +0200
+++ linux-2.4.30/drivers/i2c/i2c-dev.c	2006-12-11 18:18:37.000000000 +0100
@@ -28,7 +28,7 @@
 /* The devfs code is contributed by Philipp Matthias Hahn 
    <pmhahn@titan.lahn.de> */
 
-/* $Id: i2c-dev.c,v 1.40 2001/08/25 01:28:01 mds Exp $ */
+/* $Id: i2c-dev.c,v 1.2 2005/09/28 14:18:53 lel Exp $ */
 
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -50,6 +50,9 @@
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
 
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+
 #ifdef MODULE
 extern int init_module(void);
 extern int cleanup_module(void);
@@ -68,6 +71,11 @@
 
 static int i2cdev_release (struct inode *inode, struct file *file);
 
+#if 1
+unsigned int i2cdev_poll(struct file *file, poll_table * wait);
+int i2cdev_fasync(int fd, struct file *file, int on);
+#endif
+
 static int i2cdev_attach_adapter(struct i2c_adapter *adap);
 static int i2cdev_detach_client(struct i2c_client *client);
 static int i2cdev_command(struct i2c_client *client, unsigned int cmd,
@@ -89,6 +97,12 @@
 	ioctl:		i2cdev_ioctl,
 	open:		i2cdev_open,
 	release:	i2cdev_release,
+/* I3 Spec */
+#if 1
+	poll:           i2cdev_poll,
+	fasync:         i2cdev_fasync,
+#endif
+/* /I3 Spec */
 };
 
 #define I2CDEV_ADAPS_MAX I2C_ADAP_MAX
@@ -132,24 +146,55 @@
 #endif /* DEBUG */
 
 	struct i2c_client *client = (struct i2c_client *)file->private_data;
+	struct i2c_adapter *adap = client->adapter;
 
-	if (count > 8192)
+	if (!access_ok(VERIFY_WRITE, (void *) buf, count))
+	    return -EFAULT;
+
+	if(client->adapter->mode == I2C_SLAVE_MODE) {
+//	    printk("i2cdev: Doing slave read\n");
+	    if (adap->readbuf.readpos == adap->readbuf.writepos) {
+		if (file->f_flags & O_NONBLOCK)
+		    return -EAGAIN;
+		interruptible_sleep_on(&adap->readq);
+		if (signal_pending(current))
+		    return -ERESTART;
+	    }
+
+	    /* copy user space data to kernel space. */
+	    tmp = kmalloc(count,GFP_KERNEL);
+	    if (tmp==NULL)
+		return -ENOMEM;
+
+	    ret = i2c_slave_recv(adap, tmp, count);
+	    if (ret >= 0)
+		ret = copy_to_user(buf,tmp,count)?-EFAULT:ret;
+//	    printk("i2cdev_read(): ret = %d\n", ret);
+	    kfree(tmp);
+	} else if (client->adapter->mode == I2C_MASTER_MODE) {
+//	    printk("i2cdev: Doing master read\n");
+	    if (count > 8192)
 		count = 8192;
 
-	/* copy user space data to kernel space. */
-	tmp = kmalloc(count,GFP_KERNEL);
-	if (tmp==NULL)
+	    /* copy user space data to kernel space. */
+	    tmp = kmalloc(count,GFP_KERNEL);
+	    if (tmp==NULL)
 		return -ENOMEM;
 
 #ifdef DEBUG
-	printk(KERN_DEBUG "i2c-dev.o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),
-	       count);
+	    printk(KERN_DEBUG "i2c-dev.o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),
+		   count);
 #endif
 
-	ret = i2c_master_recv(client,tmp,count);
-	if (ret >= 0)
+	    ret = i2c_master_recv(client,tmp,count);
+	    if (ret >= 0)
 		ret = copy_to_user(buf,tmp,count)?-EFAULT:ret;
-	kfree(tmp);
+	    kfree(tmp);
+	} else {
+	    printk("i2cdev: invalid read!\n");
+	    ret = -EINVAL;
+	}
+
 	return ret;
 }
 
@@ -418,6 +463,7 @@
 static int i2cdev_release (struct inode *inode, struct file *file)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
+	i2cdev_fasync(-1, file, 0);
 	kfree(file->private_data);
 	file->private_data=NULL;
 #ifdef DEBUG
@@ -558,3 +604,36 @@
 
 #endif /* def MODULE */
 
+
+/*** BEGIN I3 Additions */
+#if 1
+unsigned int i2cdev_poll(struct file *file, poll_table * wait)
+{
+    struct i2c_client *dev = file->private_data;
+    struct i2c_adapter *adap = dev->adapter;
+
+    poll_wait(file, &adap->readq, wait);
+    if (adap->readbuf.writepos != adap->readbuf.readpos) {
+//	printk("i2cdev_poll() - returning data available\n");
+	return POLLIN | POLLRDNORM;	/* readable */
+    }
+
+    return 0;
+}
+
+int i2cdev_fasync(int fd, struct file *file, int on)
+{
+    struct i2c_client *dev = file->private_data;
+    struct i2c_adapter *adap = dev->adapter;
+    unsigned int retval;
+
+//    printk("i2cdev_fasync\n");
+
+    retval = fasync_helper(fd, file, on, &adap->fasync);
+    if (retval < 0)
+	return retval;
+
+    return 0;
+}
+#endif
+/*** END I3 Additions ***/
diff -x asm -Naur linux-2.4.30-orig/drivers/ide/Config.in linux-2.4.30/drivers/ide/Config.in
--- linux-2.4.30-orig/drivers/ide/Config.in	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/ide/Config.in	2006-12-11 18:18:37.000000000 +0100
@@ -13,7 +13,7 @@
    dep_bool '  Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE $CONFIG_X86
    define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_IDE
 
-   dep_mbool '  Support for SATA (deprecated; conflicts with libata SATA driver)' CONFIG_BLK_DEV_IDE_SATA $CONFIG_BLK_DEV_IDE
+#   dep_mbool '  Support for SATA (deprecated; conflicts with libata SATA driver)' CONFIG_BLK_DEV_IDE_SATA $CONFIG_BLK_DEV_IDE
    dep_tristate '  Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
    dep_mbool '    Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE $CONFIG_BLK_DEV_IDEDISK
    dep_mbool '    Auto-Geometry Resizing support' CONFIG_IDEDISK_STROKE $CONFIG_BLK_DEV_IDEDISK
@@ -94,8 +94,10 @@
          dep_bool '      PIO mode with DDMA assists' CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX $CONFIG_BLK_DEV_IDE_AU1XXX
          dep_bool '      Multiword DMA mode with DDMA assists' CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX $CONFIG_BLK_DEV_IDE_AU1XXX
          if [ "$CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX" = "y" ]; then
-            int '      Maximum transfer size (KB) per request (up to 128)' CONFIG_BLK_DEV_IDE_SEQTS_PER_RQ_AU1XXX 128
+            int '      Maximum transfer sectors per request (up to 128)' CONFIG_BLK_DEV_IDE_SEQTS_PER_RQ_AU1XXX 128
          fi
+         dep_bool '        IDE over PCMCIA Bus' CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX $CONFIG_BLK_DEV_IDE_AU1XXX
+         dep_bool '          Burstable PCMCIA Bus' CONFIG_BLK_DEV_IDE_PCMCIA_BURSTABLE_AU1XXX $CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX
       fi
       if [ "$CONFIG_ALL_PPC" = "y" ]; then
 	 bool '    Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
diff -x asm -Naur linux-2.4.30-orig/drivers/ide/au1xxx/au1xxx-ide.c linux-2.4.30/drivers/ide/au1xxx/au1xxx-ide.c
--- linux-2.4.30-orig/drivers/ide/au1xxx/au1xxx-ide.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/ide/au1xxx/au1xxx-ide.c	2006-12-11 18:18:37.000000000 +0100
@@ -29,25 +29,36 @@
  *  Note: for more information, please refer "AMD Alchemy Au1200/Au1550 IDE
  *        Interface and Linux Device Driver" Application Note.
  */
-
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
 #include <linux/config.h>	/* for CONFIG_BLK_DEV_IDEPCI */
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
 #include <linux/ide.h>
-#include <linux/hdreg.h>
 #include <linux/init.h>
 
 #include <asm/io.h>
 #include <asm/au1000.h>
 #include <asm/au1xxx_dbdma.h>
 
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/bus_ops.h>
+
+#define MAX_IO_WIN 2
+/* Maximum number of memory windows per socket */
+#define MAX_WIN 4
+
+// need these two defs
+#include <asm/au1000_pcmcia.h>
+#endif
+
 /*
  * Platform dependent header files
  */
@@ -60,6 +71,9 @@
 #elif defined(CONFIG_MIPS_DB1550)
 #include <asm/db1x00.h>
 
+#elif defined(CONFIG_MIPS_I3_MMB3)
+#include <asm/mood300.h>
+
 #elif defined(CONFIG_MIPS_PB1200)
 #include <asm/pb1200.h>
 
@@ -72,15 +86,34 @@
 
 #include <linux/config.h>
 
+#undef DEBUG
+#ifdef DEBUG
+#define debug_printk(...) printk(...)
+#else
+#define debug_printk(...)
+#endif
+
 #if !defined(CONFIG_BLK_DEV_IDE_PIO_AU1XXX)
 
 #if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
 #define DMA_WAIT_TIMEOUT	100
-#define NUM_DESCRIPTORS		64
-#else //CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX 
+#define NUM_DESCRIPTORS		128
+#else
 #define NUM_DESCRIPTORS		2
+#endif // end CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX
+
+/*
+ * the numb er of descriptors must be linked the number of RQSIZE; max transfer sectors 
+ * Thus NUM_DESCRIPTORS = RQSIZE must be maintained
+ */
+#if !defined(AU1XXX_ATA_RQSIZE) || !defined(CONFIG_BLK_DEV_IDE_SEQTS_PER_RQ_AU1XXX)
+#define AU1XXX_ATA_RQSIZE	NUM_DESCRIPTORS
 #endif
 
+/* set the minimum request size and its defult */
+#define AU1XXX_MIN_RQSIZE	8
+#define AUIDE_MIN_MAX(val, min, max)	(((val > max) || (val < min)) ? max : val)
+
 typedef struct
 {
 	u32			tx_dev_id, rx_dev_id;
@@ -111,24 +144,62 @@
 	{ "WDC WD200BB-00AUA1"	,	"ALL"		},
 	{0,0}
 };
-
-#if !defined(AU1XXX_ATA_RQSIZE)
-#define AU1XXX_ATA_RQSIZE	128
-#endif
 #endif /* end !CONFIG_BLK_DEV_IDE_PIO_AU1XXX */
 
 // Boot options
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+static phys_t auide_base = (phys_t)0;
+// ddma physical address
+static int auide_pcmcia_base = 0;
+#else
 static int auide_base=KSEG1ADDR(AU1XXX_ATA_PHYS_ADDR);
+#endif
 static int auide_len=AU1XXX_ATA_PHYS_LEN;
 static int auide_irq=AU1XXX_ATA_INT;
 
+//#define LASSE
+//#define LASSE0
+//#define KCN
+//#define KCN_EXPERIMENT
+
+u16 write_data[4096*4] = {0};
+
+#if !defined(CONFIG_BLK_DEV_IDE_PIO_AU1XXX)
+static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
+{
+	for ( ; drive_table->id_model ; drive_table++)
+		if ((!strcmp(drive_table->id_model, id->model)) &&
+		    ((strstr(drive_table->id_firmware, id->fw_rev)) ||
+		     (!strcmp(drive_table->id_firmware, "ALL"))))
+			return 1;
+	return 0;
+}
+#endif /* end !CONFIG_BLK_DEV_IDE_PIO_AU1XXX */
+
+/*
+ * Get the CPU ID
+ */
+static u32 get_config0(void)
+{
+        u32 temp = 0;
+  
+        asm volatile (" mfc0 %0,$16 ":"=r"(temp));
+        return temp;
+}
 
 /*
  * local I/O functions
  */
 u8 auide_inb(unsigned long port)
 {
-	return (au_readb(port));
+#ifdef MAD_DEBUG
+    u8 tmp;
+    tmp = au_readb(port);
+    printk("auide_inb: 0x%02x\n", tmp & 0xff);
+    return tmp;
+#else
+	return au_readb(port);
+#endif
 }
 
 u16 auide_inw(unsigned long port)
@@ -143,6 +214,10 @@
 
 void auide_insw(unsigned long port, void *addr, u32 count)
 {
+#ifdef LASSE0
+	printk("%s[%d]\n", __FUNCTION__, (int)count);
+#endif
+
 #if defined(CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX)
 	_auide_hwif *ahwif = &auide_hwif;
 	chan_tab_t		*ctp;
@@ -154,7 +229,7 @@
 	}
         ctp = *((chan_tab_t **)ahwif->rx_chan);
 	dp = ctp->cur_ptr;
-	while (dp->dscr_cmd0 & DSCR_CMD0_V)
+ 	while (dp->dscr_cmd0 & DSCR_CMD0_V)
 		;
         ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 #else
@@ -178,7 +253,7 @@
 
 void auide_outb(u8 addr, unsigned long port)
 {
-	return (au_writeb(addr, port));
+	au_writeb(addr, port);
 }
 
 void auide_outbsync(ide_drive_t *drive, u8 addr, unsigned long port)
@@ -198,11 +273,14 @@
 
 void auide_outsw(unsigned long port, void *addr, u32 count)
 {
+#ifdef LASSE0
+	printk("%s @%x[%d]\n", __FUNCTION__, (int)addr, (int)count);
+#endif
+
 #if defined(CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX)
 	_auide_hwif *ahwif = &auide_hwif;
 	chan_tab_t		*ctp;
 	au1x_ddma_desc_t	*dp;
-
 	if(!au1xxx_dbdma_put_source_flags(ahwif->tx_chan, (void*)addr, count << 1, DDMA_FLAGS_NOIE)) {
 		printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__);
 		return;
@@ -235,17 +313,6 @@
  * Multi-Word DMA + DDMA functions
  */
 #if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
-
-static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
-{
-	for ( ; drive_table->id_model ; drive_table++)
-		if ((!strcmp(drive_table->id_model, id->model)) &&
-		    ((strstr(drive_table->id_firmware, id->fw_rev)) ||
-		     (!strcmp(drive_table->id_firmware, "ALL"))))
-			return 1;
-	return 0;
-}
-
 static int auide_build_sglist(ide_hwif_t *hwif, struct request *rq, int data_dir)
 {
 	_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
@@ -303,6 +370,7 @@
 
 	if(nents == 0)
 		BUG();
+
 	return pci_map_sg(hwif->pci_dev, sg, nents, data_dir);
 }
 
@@ -370,6 +438,7 @@
 
 	/* fill the descriptors */
 	sg = ahwif->sg_table;
+
 	while (i && sg_dma_len(sg)) {
 		u32 cur_addr;
 		u32 cur_len;
@@ -406,6 +475,7 @@
 		sg++;
 		i--;
 	}
+
 	if (count)
 		return 1;
 use_pio_instead:
@@ -433,7 +503,9 @@
 	struct request *rq = HWGROUP(drive)->rq;
 	u8 lba48 = (drive->addressing == 1) ? 1 : 0;
 	task_ioreg_t command = WIN_NOP;
-
+#ifdef LASSE0
+	printk("%s\n", __FUNCTION__);
+#endif
 	if (!auide_build_dmatable(drive, rq, PCI_DMA_FROMDEVICE)) {
 		/* try PIO instead of DMA */
 		return 1;
@@ -441,7 +513,6 @@
 	drive->waiting_for_dma = 1;
 	if (drive->media != ide_disk)
 		return 0;
-
 	/*
 	 * FIX ME to use only ACB ide_task_t args Struct
 	 */
@@ -452,7 +523,11 @@
 	}
 
 	/* issue cmd to drive */
+#if 1 // AMD_PATCH
+	ide_execute_command(drive, command, &ide_dma_intr, WAIT_CMD, NULL);
+#else
 	ide_execute_command(drive, command, &ide_dma_intr, (2*WAIT_CMD), NULL);
+#endif
 	return auide_dma_begin(drive);
 }
 
@@ -462,6 +537,10 @@
 	u8 lba48 = (drive->addressing == 1) ? 1 : 0;
 	task_ioreg_t command = WIN_NOP;
 
+#ifdef LASSE0
+	printk("%s\n", __FUNCTION__);
+#endif
+
 	if (!auide_build_dmatable(drive, rq, PCI_DMA_TODEVICE))
 		/* try PIO instead of DMA */
 		return 1;
@@ -479,7 +558,11 @@
 	}
 
 	/* issue cmd to drive */
+#if 1 // AMD_PATCH
+	ide_execute_command(drive, command, &ide_dma_intr, WAIT_CMD, NULL);
+#else
 	ide_execute_command(drive, command, &ide_dma_intr, (2*WAIT_CMD), NULL);
+#endif
 	return auide_dma_begin(drive);
 }
 
@@ -539,6 +622,9 @@
 {
 	_auide_hwif *ahwif = (_auide_hwif*)param;
 	ahwif->drive->waiting_for_dma = 0;
+#if defined(KCN)
+	printk(">");
+#endif
 	return;
 }
 
@@ -546,6 +632,9 @@
 {
 	_auide_hwif *ahwif = (_auide_hwif*)param;
 	ahwif->drive->waiting_for_dma = 0;
+#if defined(KCN)
+	printk("<");
+#endif
 	return;
 }
 #endif /* end CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX */
@@ -554,27 +643,56 @@
 static int auide_ddma_init( _auide_hwif *auide )
 {
 	dbdev_tab_t dev;
+	au1x_ddma_desc_t *dp;
+	int i;
 #if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
 	ide_hwif_t *hwif = auide->hwif;
 #endif
 	/* Add our custom device to DDMA device table */
 	/* Create our new device entries in the table */
+	// AMD_PATCH
+	memset(&dev, '\0', sizeof(dbdev_tab_t));
+
 #if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
 	dev.dev_id = AU1XXX_ATA_DDMA_REQ;
 #else
 	dev.dev_id = DSCR_CMD0_ALWAYS;
 #endif
-	dev.dev_flags = DEV_FLAGS_OUT|DEV_FLAGS_SYNC|DEV_FLAGS_BURSTABLE;
 
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+	/*
+	 * Statis of the BURSTABLE FIFO:
+	 * Example of 4096B read.
+	 * 1) Read 512B at the first asserted DMA REQ until it is negated.
+	 * 2) After the second DMAREQ comes then the DDMA engine never resume the transaction.
+	 * Note: the transaction speed is twice slower (2 beats) than normal transaction speed (16 beats).
+	 */
+//	dev.dev_flags = DEV_FLAGS_OUT|DEV_FLAGS_SYNC|DEV_FLAGS_BURSTABLE;
+	dev.dev_flags = DEV_FLAGS_OUT|DEV_FLAGS_SYNC;
+	// we over-ride these values later
 	dev.dev_tsize = 8;
 	dev.dev_devwidth = 32;
+#else
+	dev.dev_flags = DEV_FLAGS_OUT|DEV_FLAGS_SYNC|DEV_FLAGS_BURSTABLE;
+	dev.dev_tsize = 8;
+	dev.dev_devwidth = 32;
+#endif
 
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+	dev.dev_physaddr = (u32)auide_pcmcia_base;
+	debug_printk("PCMCIA lower addr =%x\n", auide_pcmcia_base);
+#else
 	dev.dev_physaddr = (u32)AU1XXX_ATA_PHYS_ADDR;
+#endif
 	dev.dev_intlevel = 0;
 	dev.dev_intpolarity = 0;
 	auide->tx_dev_id = au1xxx_ddma_add_device( &dev );
 
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+	dev.dev_flags = DEV_FLAGS_IN|DEV_FLAGS_SYNC;
+#else
 	dev.dev_flags = DEV_FLAGS_IN|DEV_FLAGS_SYNC|DEV_FLAGS_BURSTABLE;
+#endif
 	auide->rx_dev_id = au1xxx_ddma_add_device( &dev );
 
 #if  defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
@@ -589,9 +707,51 @@
 	auide->tx_chan = au1xxx_dbdma_chan_alloc( DSCR_CMD0_ALWAYS, auide->tx_dev_id, NULL, (void*)auide );
 	auide->rx_chan = au1xxx_dbdma_chan_alloc( auide->rx_dev_id, DSCR_CMD0_ALWAYS, NULL, (void*)auide );
 #endif
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+	/* Tx/Rx src1/dest1 device width */
+	// org 16, 16
+	/* Tx/Rx src1/dest1 transfer size */
+	// org 1, 1
+	// 1, 2, 4, 8 datums
+	_au1xxx_dbdma_set_transfer_size(auide->tx_chan, 8, 8);
+	_au1xxx_dbdma_set_transfer_size(auide->rx_chan, 8, 8);
+#endif
+	_au1xxx_dbdma_set_devwidth(auide->tx_chan, 16, 16);
+	_au1xxx_dbdma_set_devwidth(auide->rx_chan, 16, 16);
+
+#if 1 // AMD_PATCH
+	/*
+	 * the number of ddma descriptor should be same as the rqsize
+	 * sanity check if the auide_probe() is not invoked yet
+	 */
+	if (!hwif->rqsize) {
+		hwif->rqsize = AUIDE_MIN_MAX(hwif->rqsize, AU1XXX_MIN_RQSIZE, AU1XXX_ATA_RQSIZE);
+	}
+
+	auide->tx_desc_head = (void*)au1xxx_dbdma_ring_alloc( auide->tx_chan, hwif->rqsize );
+	// Fix address bits 32-35 and set MEM bit for faster operation
+	dp = auide->tx_desc_head;
+	for (i=0; i<hwif->rqsize; i++) {
+	  dp->dscr_cmd0 |= DSCR_CMD0_MEM;
+	  dp->dscr_cmd1 |= DSCR_CMD1_DUPTR(0x0f);
+	  dp++;
+	}
+
+      	auide->rx_desc_head = (void*)au1xxx_dbdma_ring_alloc( auide->rx_chan, hwif->rqsize );
+	// Fix address bits 32-35 and set MEM bit for faster operation
+	dp = auide->rx_desc_head;
+	for (i=0; i<hwif->rqsize; i++) {
+	  dp->dscr_cmd0 |= DSCR_CMD0_MEM;
+	  dp->dscr_cmd1 |= DSCR_CMD1_SUPTR(0x0f);
+	  dp++;
+	}
+
+#else
 	auide->tx_desc_head = (void*)au1xxx_dbdma_ring_alloc( auide->tx_chan, NUM_DESCRIPTORS );
 	auide->rx_desc_head = (void*)au1xxx_dbdma_ring_alloc( auide->rx_chan, NUM_DESCRIPTORS );
 
+#endif
+
 #if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
 	hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
 						  PRD_ENTRIES * PRD_BYTES,	/* 1 Page */
@@ -625,6 +785,25 @@
 #endif
 	au1xxx_dbdma_start( auide->tx_chan );
 	au1xxx_dbdma_start( auide->rx_chan );
+#if 1
+	/* AMD_PATCH */
+ 	{
+		extern void dump_tlb_all(void);
+		dump_tlb_all();
+	}
+
+	debug_printk("MEM_STADDR0=%x\n", au_readl(MEM_STADDR0));
+	debug_printk("MEM_STADDR1=%x\n", au_readl(MEM_STADDR1));
+	debug_printk("MEM_STADDR2=%x\n", au_readl(MEM_STADDR2));
+	debug_printk("MEM_STADDR3=%x\n", au_readl(MEM_STADDR3));
+	debug_printk("SYS_CPUPLL=%x\n", au_readl(SYS_CPUPLL));
+	debug_printk("config0=%x\n", get_config0());
+
+// AMD_PATCH
+	/* this change must be accomplished with other clock setup including DDR, baud rate, etc. */
+//	au_writel(0x0000001c, SYS_CPUPLL); // 336
+//	au_writel(0x0000001d, SYS_CPUPLL); // 348
+#endif
 	return 0;
 }
 #endif /* end DMA + Dbdma || PIO + Dbdma*/
@@ -638,6 +817,14 @@
 		*ata_regs++ = (ide_ioreg_t) auide_base + (ide_ioreg_t)(i << AU1XXX_ATA_REG_OFFSET);
 	/* set the Alternative Status register */
 	*ata_regs = (ide_ioreg_t) auide_base + (ide_ioreg_t)(14 << AU1XXX_ATA_REG_OFFSET);
+#if 0 //defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+	printk("BASE=%x OFFSET=%d ", auide_base, AU1XXX_ATA_REG_OFFSET);
+	ata_regs = hw->io_ports;
+	for (i = 0; i < IDE_CONTROL_OFFSET; i++) {
+		printk("%x\n", *ata_regs++);
+	}
+	printk("%x\n", *ata_regs);
+#endif
 }
 
 /*
@@ -651,14 +838,17 @@
 static int auide_default_irq(ide_ioreg_t base)
 {
 	switch (base) {
-		case KSEG1ADDR(AU1XXX_ATA_PHYS_ADDR): return ((int)auide_irq);
-		case 0x1f0: return 14;
+		case 0x1f0: printk("IDE default IRQ=%d\n", auide_irq); return ((int)auide_irq);
 		case 0x170: return 15;
 		case 0x1e8: return 11;
 		case 0x168: return 10;
 		case 0x1e0: return 8;
 //		case 0x160: return 12;
 		default:
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+		  printk("%s=%x\n", __FUNCTION__, (unsigned int)base);
+			if (base == auide_base) return ((int)auide_irq);
+#endif
 			return 0;
 	}
 }
@@ -686,13 +876,28 @@
 
 	/* claim the first hwif for the Au1xxx IDE driver */
 	if (reg == auide_base) {
-		if (!auide_base)
-		{
-			printk(KERN_ERR "%s Null auide_base, data_port=%x\n", __FUNCTION__, (unsigned int)data_port);
+		if (!auide_base) {
+			debug_printk(KERN_ERR "%s Null auide_base, data_port=%x\n", __FUNCTION__, (unsigned int)data_port);
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+			/* 
+			 * map the PCMCIA SOCKET #1 I/O region
+			 * socket #0: AU1X_SOCK0_IO
+			 */
+			auide_base = (u32)ioremap(AU1X_SOCK0_IO, AU1XXX_ATA_PHYS_LEN);
+			// Lower 32 bits of the physical address for the DDMA to use
+			// Bits 32-35 is set after descriptor ring allocation
+			auide_pcmcia_base = (int)(AU1X_SOCK0_IO & 0xffffffff);
+			if (irq != NULL) {
+				debug_printk("Auide_IRQ=%x, base=%x ddma_base=%x\n",
+				       auide_irq, (unsigned int)auide_base, auide_pcmcia_base);
+				*irq = (int)auide_irq;
+			}
+#else
 			return;
+#endif
+
 		}
 		auide_setup_ports(hw);
-
 		if (irq != NULL)
 			*irq = (int)auide_irq;
 	}
@@ -757,11 +962,11 @@
 
 #if defined(CONFIG_BLK_DEV_IDE_SEQTS_PER_RQ_AU1XXX)
 	hwif->rqsize = CONFIG_BLK_DEV_IDE_SEQTS_PER_RQ_AU1XXX;
-	hwif->rqsize = ((hwif->rqsize > AU1XXX_ATA_RQSIZE) || (hwif->rqsize < 32)) ? AU1XXX_ATA_RQSIZE : hwif->rqsize;
+	/* AMD_PATCH; reduced min as 8 from 32 */ 
+	hwif->rqsize = AUIDE_MIN_MAX(hwif->rqsize, AU1XXX_MIN_RQSIZE, AU1XXX_ATA_RQSIZE);
 #else // if kernel config is not set
 	hwif->rqsize = AU1XXX_ATA_RQSIZE;
 #endif
-
 	hwif->ultra_mask = 0x0;		 /* Disable Ultra DMA */
 #if !defined(CONFIG_BLK_DEV_IDE_PIO_AU1XXX)
 	hwif->mwdma_mask = 0x07;	/* Multimode-2 DMA  */
@@ -825,10 +1030,25 @@
 	auide_hwif.hwif = hwif;
 	hwif->hwif_data = &auide_hwif;
 	auide_ddma_init(&auide_hwif);
+#if defined(CONFIG_BLK_DEV_IDE_PCMCIA_AU1XXX)
+{	
+	u32 mem_sttime0 = au_readl(MEM_STTIME0);
+	u32 mem_stcfg0 = au_readl(MEM_STCFG0);
+
+	debug_printk("Ddma RX channel mem_sttime0=%x, %x\n", mem_stcfg0, mem_sttime0);
+#if 0
+	au1xxx_dbdma_dump(auide_hwif.rx_chan);
+
+	printk("Ddma TX channel\n");
+	au1xxx_dbdma_dump(auide_hwif.tx_chan);
+#endif /* 0 */
+}
+#endif
 #endif
 	return 0;
 }
 
+#if 0
 static int __init auide_setup (char* options)
 {
 	char* this_opt;
@@ -848,6 +1068,6 @@
 
 	return 0;
 }
-__setup("auide=", auide_setup);
+#endif
 
 EXPORT_NO_SYMBOLS;
diff -x asm -Naur linux-2.4.30-orig/drivers/ide/ide-probe.c linux-2.4.30/drivers/ide/ide-probe.c
--- linux-2.4.30-orig/drivers/ide/ide-probe.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/ide/ide-probe.c	2006-12-11 18:18:37.000000000 +0100
@@ -1384,16 +1384,35 @@
 	NULL
 };
 
+static void _ide_delay_2000ms (void)
+{
+#ifndef CONFIG_BLK_DEV_IDECS
+	mdelay(2000);
+#else
+	kprint ("_ide_delay_1000ms %d\n", 1+(10 * HZ)/20);
+	__set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(1+(40 * HZ)/20);
+	//schedule_timeout(1+HZ/20);
+#endif /* CONFIG_BLK_DEV_IDECS */
+}
+
+
 int ideprobe_init (void)
 {
 	unsigned int index;
 	int probe[MAX_HWIFS];
 	
 	MOD_INC_USE_COUNT;
+
 	memset(probe, 0, MAX_HWIFS * sizeof(int));
-	for (index = 0; index < MAX_HWIFS; ++index)
-		probe[index] = !ide_hwifs[index].present;
 
+	printk ("ideprobe_init: Delaying ideprobe\n");
+	_ide_delay_2000ms();
+	printk ("ideprobe_init: Delay done\n");
+	
+	for (index = 0; index < MAX_HWIFS; ++index)
+	    probe[index] = !ide_hwifs[index].present;
+	
 	/*
 	 * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
 	 */
diff -x asm -Naur linux-2.4.30-orig/drivers/input/uinput.c linux-2.4.30/drivers/input/uinput.c
--- linux-2.4.30-orig/drivers/input/uinput.c	2005-05-12 17:57:23.000000000 +0200
+++ linux-2.4.30/drivers/input/uinput.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,7 +1,9 @@
 /*
  *  User level driver support for input subsystem
+ *  with scancode parser from drivers/char/pc_keyb.c
  *
  * Heavily based on evdev.c by Vojtech Pavlik
+ * scancode parser added by Anders Nystrom, South Pole, for i3 micro
  *
  * 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
@@ -34,6 +36,8 @@
 #include <linux/miscdevice.h>
 #include <linux/uinput.h>
 
+
+
 static int uinput_dev_open(struct input_dev *dev)
 {
 	return 0;
@@ -235,6 +239,24 @@
 
 		if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
 			return -EFAULT;
+
+		/* i3 */
+		if (ev.type == EV_MSC && ev.code == MSC_SCAN) {
+			/*
+			 * translate EV_MSC/MSC_SCAN/scancode
+			 * to EV_KEY/<keycode>/<make/break>
+			 */
+			unsigned char keycode;
+
+			if (pckbd_translate(ev.value, &keycode, 1)) {
+				ev.type = EV_KEY;
+				ev.code = keycode;
+				ev.value = (ev.value&0x80) ? 0 : 1;
+			} else {
+				return count;
+			}
+		}
+		/* /i3 */
 		input_event(udev->dev, ev.type, ev.code, ev.value);
 	}
 	else
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/Config.in linux-2.4.30/drivers/mtd/Config.in
--- linux-2.4.30-orig/drivers/mtd/Config.in	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/Config.in	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 
-# $Id: Config.in,v 1.76 2004/04/01 10:17:40 gthomas Exp $
+# $Id: Config.in,v 1.2 2005/09/28 14:18:53 lel Exp $
 
 mainmenu_option next_comment
 comment 'Memory Technology Devices (MTD)'
@@ -11,14 +11,16 @@
    if [ "$CONFIG_MTD_DEBUG" = "y" ]; then
       int '  Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0
    fi
-   dep_tristate '  MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD
+   bool '  MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD
    dep_tristate '  MTD concatenating support' CONFIG_MTD_CONCAT $CONFIG_MTD
    dep_tristate '  RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS
    if [ "$CONFIG_MTD_REDBOOT_PARTS" = "y" -o "$CONFIG_MTD_REDBOOT_PARTS" = "m" ]; then
       bool '  Include unallocated flash space' CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
       bool '  Force read-only for RedBoot system images' CONFIG_MTD_REDBOOT_PARTS_READONLY
    fi
-   dep_tristate '  Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS $CONFIG_MTD_PARTITIONS
+   if [ "$CONFIG_MTD_PARTITIONS" = "y" ]; then
+      bool '  Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS 
+   fi
    if [ "$CONFIG_ARM" = "y" ]; then
       dep_tristate '  ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS
    fi
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/Makefile linux-2.4.30/drivers/mtd/Makefile
--- linux-2.4.30-orig/drivers/mtd/Makefile	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -1,58 +1,43 @@
 #
-# Makefile for the memory technology device drivers.
+# linux/drivers/Makefile.24
+# Makefile for obsolete kernels.
 #
-# $Id: Makefile.common,v 1.2 2003/05/23 11:38:29 dwmw2 Exp $
-
-#                       *** BIG UGLY NOTE ***
-#
-# The shiny new inter_module_xxx has introduced yet another ugly link
-# order dependency, which I'd previously taken great care to avoid.
-# We now have to ensure that the chip drivers are initialised before the
-# map drivers, and that the doc200[01] drivers are initialised before
-# docprobe.
-#
-# We'll hopefully merge the doc200[01] drivers and docprobe back into
-# a single driver some time soon, but the CFI drivers are going to have
-# to stay like that.
-#
-# Urgh.
-# 
-# dwmw2 21/11/0
+# $Id: Makefile,v 1.2 2005/09/28 14:18:53 lel Exp $
 
 # Core functionality.
-obj-$(CONFIG_MTD)		+= mtdcore.o
-obj-$(CONFIG_MTD_CONCAT)	+= mtdconcat.o
-obj-$(CONFIG_MTD_PARTITIONS)	+= mtdpart.o
+mtd-y				:= mtdcore.o
+mtd-$(CONFIG_MTD_PARTITIONS)	+= mtdpart.o
+obj-$(CONFIG_MTD)		+= $(mtd-y)
+
+obj-$(CONFIG_MTD_CONCAT)        += mtdconcat.o
 obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
 obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
-obj-$(CONFIG_MTD_AFS_PARTS)	+= afs.o
+obj-$(CONFIG_MTD_AFS_PARTS)     += afs.o
 
 # 'Users' - code which presents functionality to userspace.
-obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
-obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o mtd_blkdevs.o
-obj-$(CONFIG_MTD_BLOCK_RO)	+= mtdblock_ro.o mtd_blkdevs.o
-obj-$(CONFIG_FTL)		+= ftl.o mtd_blkdevs.o
-obj-$(CONFIG_NFTL)		+= nftl.o mtd_blkdevs.o
-obj-$(CONFIG_INFTL)		+= inftl.o mtd_blkdevs.o
-
-nftl-objs		:= nftlcore.o nftlmount.o
-inftl-objs		:= inftlcore.o inftlmount.o
+obj-$(CONFIG_MTD_CHAR)          += mtdchar.o
+obj-$(CONFIG_MTD_BLOCK)         += mtdblock.o mtd_blkdevs-24.o
+obj-$(CONFIG_MTD_BLOCK_RO)      += mtdblock_ro.o mtd_blkdevs-24.o
+obj-$(CONFIG_FTL)               += ftl.o mtd_blkdevs-24.o
+obj-$(CONFIG_NFTL)              += nftl.o mtd_blkdevs-24.o
+obj-$(CONFIG_INFTL)             += inftl.o mtd_blkdevs-24.o
 
-ifeq ($(PATCHLEVEL),4)
+nftl-objs               := nftlcore.o nftlmount.o
+inftl-objs              := inftlcore.o inftlmount.o
 
 export-objs	:= mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o \
 		   mtdconcat.o mtd_blkdevs-24.o
 
-mtd_blkdevs-objs	:= mtd_blkdevs-24.o
+mtd_blkdevs-objs := mtd_blkdevs-24.o
 
-obj-y           += chips/chipslink.o maps/mapslink.o \
-			devices/devlink.o nand/nandlink.o
+obj-y		+= chips/chipslink.o maps/mapslink.o \
+		   devices/devlink.o nand/nandlink.o
 
 O_TARGET	:= mtdlink.o
 
-list-multi	:= nftl.o inftl.o mtd_blkdevs.o
+list-multi	:= nftl.o inftl.o mtd_blkdevs-24.o
 
-mod-subdirs	:= 
+mod-subdirs	:=
 subdir-y	:= chips maps devices nand
 subdir-m	:= $(subdir-y)
 
@@ -67,6 +52,3 @@
 mtd_blkdevs.o: $(mtd_blkdevs-objs)
 	$(LD) -r -o $@ $(mtd_blkdevs-objs)
 
-else
-obj-y		+= chips/ maps/ devices/ nand/
-endif
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/Makefile.common linux-2.4.30/drivers/mtd/Makefile.common
--- linux-2.4.30-orig/drivers/mtd/Makefile.common	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/Makefile.common	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,27 @@
+#
+# Makefile for the memory technology device drivers.
+#
+# $Id: Makefile.common,v 1.2 2005/09/28 14:18:53 lel Exp $
+
+# Core functionality.
+mtd-y				:= mtdcore.o
+mtd-$(CONFIG_MTD_PARTITIONS)	+= mtdpart.o
+obj-$(CONFIG_MTD)		+= $(mtd-y)
+
+obj-$(CONFIG_MTD_CONCAT)	+= mtdconcat.o
+obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
+obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
+obj-$(CONFIG_MTD_AFS_PARTS)	+= afs.o
+
+# 'Users' - code which presents functionality to userspace.
+obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
+obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o mtd_blkdevs.o
+obj-$(CONFIG_MTD_BLOCK_RO)	+= mtdblock_ro.o mtd_blkdevs.o
+obj-$(CONFIG_FTL)		+= ftl.o mtd_blkdevs.o
+obj-$(CONFIG_NFTL)		+= nftl.o mtd_blkdevs.o
+obj-$(CONFIG_INFTL)		+= inftl.o mtd_blkdevs.o
+
+nftl-objs		:= nftlcore.o nftlmount.o
+inftl-objs		:= inftlcore.o inftlmount.o
+
+obj-y		+= chips/ maps/ devices/ nand/
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/afs.c linux-2.4.30/drivers/mtd/afs.c
--- linux-2.4.30-orig/drivers/mtd/afs.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/afs.c	2006-12-11 18:18:37.000000000 +0100
@@ -21,7 +21,7 @@
    This is access code for flashes using ARM's flash partitioning 
    standards.
 
-   $Id: afs.c,v 1.13 2004/02/27 22:09:59 rmk Exp $
+   $Id: afs.c,v 1.2 2005/09/28 14:18:53 lel Exp $
 
 ======================================================================*/
 
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/Config.in linux-2.4.30/drivers/mtd/chips/Config.in
--- linux-2.4.30-orig/drivers/mtd/chips/Config.in	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/Config.in	2006-12-11 18:18:37.000000000 +0100
@@ -1,6 +1,6 @@
 # drivers/mtd/chips/Config.in
 
-# $Id: Config.in,v 1.19 2004/04/15 14:24:09 thayne Exp $
+# $Id: Config.in,v 1.2 2005/09/28 14:18:59 lel Exp $
 
 mainmenu_option next_comment
 
@@ -28,11 +28,13 @@
 	 LITTLE_ENDIAN_BYTE	CONFIG_MTD_CFI_LE_BYTE_SWAP"	NO
      bool '  Specific CFI Flash geometry selection' CONFIG_MTD_CFI_GEOMETRY
      if [ "$CONFIG_MTD_CFI_GEOMETRY" = "y" ]; then
-       bool '    Support  8-bit buswidth' CONFIG_MTD_CFI_B1
-       bool '    Support 16-bit buswidth' CONFIG_MTD_CFI_B2
-       bool '    Support 32-bit buswidth' CONFIG_MTD_CFI_B4
-       bool '    Support 64-bit buswidth' CONFIG_MTD_CFI_B8
-	 if [ "$CONFIG_MTD_CFI_B1" = "y" ]; then
+       bool '    Support   8-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_1
+       bool '    Support  16-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_2
+       bool '    Support  32-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_4
+       bool '    Support  64-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_8
+       bool '    Support 128-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_16
+       bool '    Support 256-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_32
+	 if [ "$CONFIG_MTD_MAP_BANK_WIDTH_1" = "y" ]; then
 	   define_bool CONFIG_MTD_CFI_I1 y
          else
            bool '    Support 1-chip flash interleave' CONFIG_MTD_CFI_I1
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/Makefile linux-2.4.30/drivers/mtd/chips/Makefile
--- linux-2.4.30-orig/drivers/mtd/chips/Makefile	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -1,33 +1,12 @@
 #
-# linux/drivers/chips/Makefile
+# linux/drivers/chips/Makefile.24
+# Makefile for obsolete kernels.
 #
-# $Id: Makefile.common,v 1.3 2003/09/25 14:40:34 thayne Exp $
+# $Id: Makefile,v 1.2 2005/09/28 14:18:59 lel Exp $
 
-ifeq ($(PATCHLEVEL),4)
 O_TARGET	:= chipslink.o
 export-objs	:= chipreg.o gen_probe.o cfi_util.o
-endif
 
-#                       *** BIG UGLY NOTE ***
-#
-# The removal of get_module_symbol() and replacement with
-# inter_module_register() et al has introduced a link order dependency
-# here where previously there was none.  We now have to ensure that
-# the CFI command set drivers are linked before cfi_probe.o
-
-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
+include Makefile.common
 
--include $(TOPDIR)/Rules.make
+include $(TOPDIR)/Rules.make
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/Makefile.common linux-2.4.30/drivers/mtd/chips/Makefile.common
--- linux-2.4.30-orig/drivers/mtd/chips/Makefile.common	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/chips/Makefile.common	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,26 @@
+#
+# linux/drivers/chips/Makefile
+#
+# $Id: Makefile.common,v 1.2 2005/09/28 14:18:59 lel Exp $
+
+#                       *** BIG UGLY NOTE ***
+#
+# The removal of get_module_symbol() and replacement with
+# inter_module_register() et al has introduced a link order dependency
+# here where previously there was none.  We now have to ensure that
+# the CFI command set drivers are linked before gen_probe.o
+
+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 -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/amd_flash.c linux-2.4.30/drivers/mtd/chips/amd_flash.c
--- linux-2.4.30-orig/drivers/mtd/chips/amd_flash.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/amd_flash.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,7 +3,7 @@
  *
  * Author: Jonas Holmberg <jonas.holmberg@axis.com>
  *
- * $Id: amd_flash.c,v 1.23 2003/06/12 09:24:13 dwmw2 Exp $
+ * $Id: amd_flash.c,v 1.3 2005/10/12 14:47:50 lel Exp $
  *
  * Copyright (c) 2001 Axis Communications AB
  *
@@ -67,7 +67,6 @@
 #define AM29LV160DT	0x22C4
 #define AM29LV160DB	0x2249
 #define AM29BDS323D     0x22D1
-#define AM29BDS643D	0x227E
 
 /* Atmel */
 #define AT49xV16x	0x00C0
@@ -618,17 +617,6 @@
 			{ .offset = 0x3f0000, .erasesize = 0x02000, .numblocks =  8 },
 		}
 	}, {
-		.mfr_id = MANUFACTURER_AMD,
-		.dev_id = AM29BDS643D,
-		.name = "AMD AM29BDS643D",
-		.size = 0x00800000,
-		.numeraseregions = 3,
-		.regions = {
-			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 },
-			{ .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 },
-			{ .offset = 0x7f0000, .erasesize = 0x02000, .numblocks =  8 },
-		}
-	}, {
 		.mfr_id = MANUFACTURER_ATMEL,
 		.dev_id = AT49xV16x,
 		.name = "Atmel AT49xV16x",
@@ -718,7 +706,7 @@
 		       "memory for MTD erase region info\n", map->name);
 		kfree(mtd);
 		map->fldrv_priv = NULL;
-		return 0;
+		return NULL;
 	}
 
 	reg_idx = 0;
@@ -780,8 +768,8 @@
 	map->fldrv_priv = private;
 
 	map->fldrv = &amd_flash_chipdrv;
-	MOD_INC_USE_COUNT;
 
+	__module_get(THIS_MODULE);
 	return mtd;
 }
 
@@ -1122,7 +1110,7 @@
 	timeo = jiffies + (HZ * 20);
 
 	spin_unlock_bh(chip->mutex);
-	schedule_timeout(HZ);
+	msleep(1000);
 	spin_lock_bh(chip->mutex);
 	
 	while (flash_is_busy(map, adr, private->interleave)) {
@@ -1307,9 +1295,7 @@
 	}
 		
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback) {
-		instr->callback(instr);
-	}
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/cfi_cmdset_0001.c linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0001.c
--- linux-2.4.30-orig/drivers/mtd/chips/cfi_cmdset_0001.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0001.c	2006-12-11 18:18:37.000000000 +0100
@@ -4,7 +4,7 @@
  *
  * (C) 2000 Red Hat. GPL'd
  *
- * $Id: cfi_cmdset_0001.c,v 1.135 2004/03/25 13:24:40 dwmw2 Exp $
+ * $Id: cfi_cmdset_0001.c,v 1.3 2005/10/12 14:47:50 lel Exp $
  *
  * 
  * 10/10/2000	Nicolas Pitre <nico@cam.org>
@@ -29,39 +29,64 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/mtd/xip.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/cfi.h>
 
 /* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
+/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */
 
 // debugging, turns off buffer write mode if set to 1
 #define FORCE_WORD_WRITE 0
 
+#define MANUFACTURER_INTEL	0x0089
+#define I82802AB	0x00ad
+#define I82802AC	0x00ac
+#define MANUFACTURER_ST         0x0020
+#define M50LPW080       0x002F
+
 static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
-static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
-static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
 static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+static int cfi_intelext_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *);
 static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
 static void cfi_intelext_sync (struct mtd_info *);
 static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
 static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
+#ifdef CONFIG_MTD_OTP
+static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
+static int cfi_intelext_get_fact_prot_info (struct mtd_info *,
+					    struct otp_info *, size_t);
+static int cfi_intelext_get_user_prot_info (struct mtd_info *,
+					    struct otp_info *, size_t);
+#endif
 static int cfi_intelext_suspend (struct mtd_info *);
 static void cfi_intelext_resume (struct mtd_info *);
+static int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *);
 
 static void cfi_intelext_destroy(struct mtd_info *);
 
 struct mtd_info *cfi_cmdset_0001(struct map_info *, int);
 
-static struct mtd_info *cfi_intelext_setup (struct map_info *);
+static struct mtd_info *cfi_intelext_setup (struct mtd_info *);
+static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **);
 
 static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
 		     size_t *retlen, u_char **mtdbuf);
 static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
 			size_t len);
 
+static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
+static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
+#include "fwh_lock.h"
+
+
 
 /*
  *  *********** SETUP AND PROBE BITS  ***********
@@ -81,17 +106,20 @@
 static void cfi_tell_features(struct cfi_pri_intelext *extp)
 {
 	int i;
-	printk("  Feature/Command Support: %4.4X\n", extp->FeatureSupport);
-	printk("     - Chip Erase:         %s\n", extp->FeatureSupport&1?"supported":"unsupported");
-	printk("     - Suspend Erase:      %s\n", extp->FeatureSupport&2?"supported":"unsupported");
-	printk("     - Suspend Program:    %s\n", extp->FeatureSupport&4?"supported":"unsupported");
-	printk("     - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported");
-	printk("     - Queued Erase:       %s\n", extp->FeatureSupport&16?"supported":"unsupported");
-	printk("     - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported");
-	printk("     - Protection Bits:    %s\n", extp->FeatureSupport&64?"supported":"unsupported");
-	printk("     - Page-mode read:     %s\n", extp->FeatureSupport&128?"supported":"unsupported");
-	printk("     - Synchronous read:   %s\n", extp->FeatureSupport&256?"supported":"unsupported");
-	for (i=9; i<32; i++) {
+	printk("  Extended Query version %c.%c\n", extp->MajorVersion, extp->MinorVersion);
+	printk("  Feature/Command Support:      %4.4X\n", extp->FeatureSupport);
+	printk("     - Chip Erase:              %s\n", extp->FeatureSupport&1?"supported":"unsupported");
+	printk("     - Suspend Erase:           %s\n", extp->FeatureSupport&2?"supported":"unsupported");
+	printk("     - Suspend Program:         %s\n", extp->FeatureSupport&4?"supported":"unsupported");
+	printk("     - Legacy Lock/Unlock:      %s\n", extp->FeatureSupport&8?"supported":"unsupported");
+	printk("     - Queued Erase:            %s\n", extp->FeatureSupport&16?"supported":"unsupported");
+	printk("     - Instant block lock:      %s\n", extp->FeatureSupport&32?"supported":"unsupported");
+	printk("     - Protection Bits:         %s\n", extp->FeatureSupport&64?"supported":"unsupported");
+	printk("     - Page-mode read:          %s\n", extp->FeatureSupport&128?"supported":"unsupported");
+	printk("     - Synchronous read:        %s\n", extp->FeatureSupport&256?"supported":"unsupported");
+	printk("     - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported");
+	printk("     - Extended Flash Array:    %s\n", extp->FeatureSupport&1024?"supported":"unsupported");
+	for (i=11; i<32; i++) {
 		if (extp->FeatureSupport & (1<<i)) 
 			printk("     - Unknown Bit %X:      supported\n", i);
 	}
@@ -105,12 +133,18 @@
 	
 	printk("  Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask);
 	printk("     - Lock Bit Active:      %s\n", extp->BlkStatusRegMask&1?"yes":"no");
-	printk("     - Valid Bit Active:     %s\n", extp->BlkStatusRegMask&2?"yes":"no");
-	for (i=2; i<16; i++) {
+	printk("     - Lock-Down Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no");
+	for (i=2; i<3; i++) {
 		if (extp->BlkStatusRegMask & (1<<i))
 			printk("     - Unknown Bit %X Active: yes\n",i);
 	}
-	
+	printk("     - EFA Lock Bit:         %s\n", extp->BlkStatusRegMask&16?"yes":"no");
+	printk("     - EFA Lock-Down Bit:    %s\n", extp->BlkStatusRegMask&32?"yes":"no");
+	for (i=6; i<16; i++) {
+		if (extp->BlkStatusRegMask & (1<<i))
+			printk("     - Unknown Bit %X Active: yes\n",i);
+	}
+
 	printk("  Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", 
 	       extp->VccOptimal >> 4, extp->VccOptimal & 0xf);
 	if (extp->VppOptimal)
@@ -121,8 +155,9 @@
 
 #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
 /* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ 
-static void fixup_intel_strataflash(struct map_info *map, void* param)
+static void fixup_intel_strataflash(struct mtd_info *mtd, void* param)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
 
@@ -132,16 +167,32 @@
 }
 #endif
 
-static void fixup_st_m28w320ct(struct map_info *map, void* param)
+#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND
+static void fixup_no_write_suspend(struct mtd_info *mtd, void* param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
+
+	if (cfip && (cfip->FeatureSupport&4)) {
+		cfip->FeatureSupport &= ~4;
+		printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n");
+	}
+}
+#endif
+
+static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	
 	cfi->cfiq->BufWriteTimeoutTyp = 0;	/* Not supported */
 	cfi->cfiq->BufWriteTimeoutMax = 0;	/* Not supported */
 }
 
-static void fixup_st_m28w320cb(struct map_info *map, void* param)
+static void fixup_st_m28w320cb(struct mtd_info *mtd, void* param)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	
 	/* Note this is done after the region info is endian swapped */
@@ -149,25 +200,133 @@
 		(cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;
 };
 
-static struct cfi_fixup fixup_table[] = {
+static void fixup_use_point(struct mtd_info *mtd, void *param)
+{
+	struct map_info *map = mtd->priv;
+	if (!mtd->point && map_is_linear(map)) {
+		mtd->point   = cfi_intelext_point;
+		mtd->unpoint = cfi_intelext_unpoint;
+	}
+}
+
+static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	if (cfi->cfiq->BufWriteTimeoutTyp) {
+		printk(KERN_INFO "Using buffer write method\n" );
+		mtd->write = cfi_intelext_write_buffers;
+		mtd->writev = cfi_intelext_writev;
+	}
+}
+
+static struct cfi_fixup cfi_fixup_table[] = {
 #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
-	{
-		CFI_MFR_ANY, CFI_ID_ANY,
-		fixup_intel_strataflash, NULL
-	}, 
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, 
 #endif
-	{
-		0x0020,	/* STMicroelectronics */
-		0x00ba,	/* M28W320CT */
-		fixup_st_m28w320ct, NULL
-	}, {
-		0x0020,	/* STMicroelectronics */
-		0x00bb,	/* M28W320CB */
-		fixup_st_m28w320cb, NULL
-	}, {
-		0, 0, NULL, NULL
-	}
+#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend, NULL },
+#endif
+#if !FORCE_WORD_WRITE
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL },
+#endif
+	{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
+	{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
+	{ 0, 0, NULL, NULL }
+};
+
+static struct cfi_fixup jedec_fixup_table[] = {
+	{ MANUFACTURER_INTEL, I82802AB,   fixup_use_fwh_lock, NULL, },
+	{ MANUFACTURER_INTEL, I82802AC,   fixup_use_fwh_lock, NULL, },
+	{ MANUFACTURER_ST,    M50LPW080,  fixup_use_fwh_lock, NULL, },
+	{ 0, 0, NULL, NULL }
 };
+static struct cfi_fixup fixup_table[] = {
+	/* The CFI vendor ids and the JEDEC vendor IDs appear
+	 * to be common.  It is like the devices id's are as
+	 * well.  This table is to pick all cases where
+	 * we know that is the case.
+	 */
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point, NULL },
+	{ 0, 0, NULL, NULL }
+};
+
+static inline struct cfi_pri_intelext *
+read_pri_intelext(struct map_info *map, __u16 adr)
+{
+	struct cfi_pri_intelext *extp;
+	unsigned int extp_size = sizeof(*extp);
+
+ again:
+	extp = (struct cfi_pri_intelext *)cfi_read_pri(map, adr, extp_size, "Intel/Sharp");
+	if (!extp)
+		return NULL;
+
+	if (extp->MajorVersion != '1' ||
+	    (extp->MinorVersion < '0' || extp->MinorVersion > '4')) {
+		printk(KERN_ERR "  Unknown Intel/Sharp Extended Query "
+		       "version %c.%c.\n",  extp->MajorVersion,
+		       extp->MinorVersion);
+		kfree(extp);
+		return NULL;
+	}
+
+	/* Do some byteswapping if necessary */
+	extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport);
+	extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask);
+	extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr);
+
+	if (extp->MajorVersion == '1' && extp->MinorVersion >= '3') {
+		unsigned int extra_size = 0;
+		int nb_parts, i;
+
+		/* Protection Register info */
+		extra_size += (extp->NumProtectionFields - 1) *
+			      sizeof(struct cfi_intelext_otpinfo);
+
+		/* Burst Read info */
+		extra_size += (extp->MinorVersion < '4') ? 6 : 5;
+
+		/* Number of hardware-partitions */
+		extra_size += 1;
+		if (extp_size < sizeof(*extp) + extra_size)
+			goto need_more;
+		nb_parts = extp->extra[extra_size - 1];
+
+		/* skip the sizeof(partregion) field in CFI 1.4 */
+		if (extp->MinorVersion >= '4')
+			extra_size += 2;
+
+		for (i = 0; i < nb_parts; i++) {
+			struct cfi_intelext_regioninfo *rinfo;
+			rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[extra_size];
+			extra_size += sizeof(*rinfo);
+			if (extp_size < sizeof(*extp) + extra_size)
+				goto need_more;
+			rinfo->NumIdentPartitions=le16_to_cpu(rinfo->NumIdentPartitions);
+			extra_size += (rinfo->NumBlockTypes - 1)
+				      * sizeof(struct cfi_intelext_blockinfo);
+		}
+
+		if (extp->MinorVersion >= '4')
+			extra_size += sizeof(struct cfi_intelext_programming_regioninfo);
+
+		if (extp_size < sizeof(*extp) + extra_size) {
+			need_more:
+			extp_size = sizeof(*extp) + extra_size;
+			kfree(extp);
+			if (extp_size > 4096) {
+				printk(KERN_ERR
+					"%s: cfi_pri_intelext is too fat\n",
+					__FUNCTION__);
+				return NULL;
+			}
+			goto again;
+		}
+	}
+		
+	return extp;
+}
 
 /* This routine is made available to other mtd code via
  * inter_module_register.  It must only be accessed through
@@ -179,8 +338,32 @@
 struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
+	struct mtd_info *mtd;
 	int i;
 
+	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	if (!mtd) {
+		printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+		return NULL;
+	}
+	memset(mtd, 0, sizeof(*mtd));
+	mtd->priv = map;
+	mtd->type = MTD_NORFLASH;
+
+	/* Fill in the default mtd operations */
+	mtd->erase   = cfi_intelext_erase_varsize;
+	mtd->read    = cfi_intelext_read;
+	mtd->write   = cfi_intelext_write_words;
+	mtd->sync    = cfi_intelext_sync;
+	mtd->lock    = cfi_intelext_lock;
+	mtd->unlock  = cfi_intelext_unlock;
+	mtd->suspend = cfi_intelext_suspend;
+	mtd->resume  = cfi_intelext_resume;
+	mtd->flags   = MTD_CAP_NORFLASH;
+	mtd->name    = map->name;
+
+	mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
+
 	if (cfi->cfi_mode == CFI_MODE_CFI) {
 		/* 
 		 * It's a real CFI chip, not one for which the probe
@@ -190,20 +373,17 @@
 		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
 		struct cfi_pri_intelext *extp;
 
-		extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp");
-		if (!extp)
+		extp = read_pri_intelext(map, adr);
+		if (!extp) {
+			kfree(mtd);
 			return NULL;
-		
-		/* Do some byteswapping if necessary */
-		extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport);
-		extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask);
-		extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr);
+		}
 
 		/* Install our own private info structure */
 		cfi->cmdset_priv = extp;	
 
-		cfi_fixup(map, fixup_table);
-			
+		cfi_fixup(mtd, cfi_fixup_table);
+
 #ifdef DEBUG_CFI_FEATURES
 		/* Tell the user about it in lots of lovely detail */
 		cfi_tell_features(extp);
@@ -213,6 +393,12 @@
 			printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n");
 		}
 	}
+	else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
+		/* Apply jedec specific fixups */
+		cfi_fixup(mtd, jedec_fixup_table);
+	}
+	/* Apply generic fixups */
+	cfi_fixup(mtd, fixup_table);
 
 	for (i=0; i< cfi->numchips; i++) {
 		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
@@ -223,28 +409,19 @@
 
 	map->fldrv = &cfi_intelext_chipdrv;
 	
-	return cfi_intelext_setup(map);
+	return cfi_intelext_setup(mtd);
 }
 
-static struct mtd_info *cfi_intelext_setup(struct map_info *map)
+static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct mtd_info *mtd;
 	unsigned long offset = 0;
 	int i,j;
 	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
 	//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
 
-	if (!mtd) {
-		printk(KERN_ERR "Failed to allocate memory for MTD device\n");
-		goto setup_err;
-	}
-
-	memset(mtd, 0, sizeof(*mtd));
-	mtd->priv = map;
-	mtd->type = MTD_NORFLASH;
 	mtd->size = devsize * cfi->numchips;
 
 	mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
@@ -278,39 +455,28 @@
 	}
 
 	for (i=0; i<mtd->numeraseregions;i++){
-		printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n",
+		printk(KERN_DEBUG "erase region %d: offset=0x%x,size=0x%x,blocks=%d\n",
 		       i,mtd->eraseregions[i].offset,
 		       mtd->eraseregions[i].erasesize,
 		       mtd->eraseregions[i].numblocks);
 	}
 
-	/* Also select the correct geometry setup too */ 
-	mtd->erase = cfi_intelext_erase_varsize;
-	mtd->read = cfi_intelext_read;
+#ifdef CONFIG_MTD_OTP
+	mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
+	mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
+	mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg;
+	mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
+	mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info;
+	mtd->get_user_prot_info = cfi_intelext_get_user_prot_info;
+#endif
 
-	if (map_is_linear(map)) {
-		mtd->point = cfi_intelext_point;
-		mtd->unpoint = cfi_intelext_unpoint;
-	}
+	/* This function has the potential to distort the reality
+	   a bit and therefore should be called last. */
+	if (cfi_intelext_partition_fixup(mtd, &cfi) != 0)
+		goto setup_err;
 
-	if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
-		printk(KERN_INFO "Using buffer write method\n" );
-		mtd->write = cfi_intelext_write_buffers;
-	} else {
-		printk(KERN_INFO "Using word write method\n" );
-		mtd->write = cfi_intelext_write_words;
-	}
-	mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
-	mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
-	mtd->sync = cfi_intelext_sync;
-	mtd->lock = cfi_intelext_lock;
-	mtd->unlock = cfi_intelext_unlock;
-	mtd->suspend = cfi_intelext_suspend;
-	mtd->resume = cfi_intelext_resume;
-	mtd->flags = MTD_CAP_NORFLASH;
-	map->fldrv = &cfi_intelext_chipdrv;
-	mtd->name = map->name;
 	__module_get(THIS_MODULE);
+	register_reboot_notifier(&mtd->reboot_notifier);
 	return mtd;
 
  setup_err:
@@ -320,10 +486,131 @@
 		kfree(mtd);
 	}
 	kfree(cfi->cmdset_priv);
-	kfree(cfi->cfiq);
 	return NULL;
 }
 
+static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
+					struct cfi_private **pcfi)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = *pcfi;
+	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+
+	/*
+	 * Probing of multi-partition flash ships.
+	 *
+	 * To support multiple partitions when available, we simply arrange
+	 * for each of them to have their own flchip structure even if they
+	 * are on the same physical chip.  This means completely recreating
+	 * a new cfi_private structure right here which is a blatent code
+	 * layering violation, but this is still the least intrusive
+	 * arrangement at this point. This can be rearranged in the future
+	 * if someone feels motivated enough.  --nico
+	 */
+	if (extp && extp->MajorVersion == '1' && extp->MinorVersion >= '3'
+	    && extp->FeatureSupport & (1 << 9)) {
+		struct cfi_private *newcfi;
+		struct flchip *chip;
+		struct flchip_shared *shared;
+		int offs, numregions, numparts, partshift, numvirtchips, i, j;
+
+		/* Protection Register info */
+		offs = (extp->NumProtectionFields - 1) *
+		       sizeof(struct cfi_intelext_otpinfo);
+
+		/* Burst Read info */
+		offs += (extp->MinorVersion < '4') ? 6 : 5;
+
+		/* Number of partition regions */
+		numregions = extp->extra[offs];
+		offs += 1;
+
+		/* skip the sizeof(partregion) field in CFI 1.4 */
+		if (extp->MinorVersion >= '4')
+			offs += 2;
+
+		/* Number of hardware partitions */
+		numparts = 0;
+		for (i = 0; i < numregions; i++) {
+			struct cfi_intelext_regioninfo *rinfo;
+			rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[offs];
+			numparts += rinfo->NumIdentPartitions;
+			offs += sizeof(*rinfo)
+				+ (rinfo->NumBlockTypes - 1) *
+				  sizeof(struct cfi_intelext_blockinfo);
+		}
+
+		/* Programming Region info */
+		if (extp->MinorVersion >= '4') {
+			struct cfi_intelext_programming_regioninfo *prinfo;
+			prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];
+			MTD_PROGREGION_SIZE(mtd) = cfi->interleave << prinfo->ProgRegShift;
+			MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid;
+			MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid;
+			mtd->flags |= MTD_PROGRAM_REGIONS;
+			printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",
+			       map->name, MTD_PROGREGION_SIZE(mtd),
+			       MTD_PROGREGION_CTRLMODE_VALID(mtd),
+			       MTD_PROGREGION_CTRLMODE_INVALID(mtd));
+		}
+
+		/*
+		 * All functions below currently rely on all chips having
+		 * the same geometry so we'll just assume that all hardware
+		 * partitions are of the same size too.
+		 */
+		partshift = cfi->chipshift - __ffs(numparts);
+
+		if ((1 << partshift) < mtd->erasesize) {
+			printk( KERN_ERR
+				"%s: bad number of hw partitions (%d)\n",
+				__FUNCTION__, numparts);
+			return -EINVAL;
+		}
+
+		numvirtchips = cfi->numchips * numparts;
+		newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL);
+		if (!newcfi)
+			return -ENOMEM;
+		shared = kmalloc(sizeof(struct flchip_shared) * cfi->numchips, GFP_KERNEL);
+		if (!shared) {
+			kfree(newcfi);
+			return -ENOMEM;
+		}
+		memcpy(newcfi, cfi, sizeof(struct cfi_private));
+		newcfi->numchips = numvirtchips;
+		newcfi->chipshift = partshift;
+
+		chip = &newcfi->chips[0];
+		for (i = 0; i < cfi->numchips; i++) {
+			shared[i].writing = shared[i].erasing = NULL;
+			spin_lock_init(&shared[i].lock);
+			for (j = 0; j < numparts; j++) {
+				*chip = cfi->chips[i];
+				chip->start += j << partshift;
+				chip->priv = &shared[i];
+				/* those should be reset too since
+				   they create memory references. */
+				init_waitqueue_head(&chip->wq);
+				spin_lock_init(&chip->_spinlock);
+				chip->mutex = &chip->_spinlock;
+				chip++;
+			}
+		}
+
+		printk(KERN_DEBUG "%s: %d set(s) of %d interleaved chips "
+				  "--> %d partitions of %d KiB\n",
+				  map->name, cfi->numchips, cfi->interleave,
+				  newcfi->numchips, 1<<(newcfi->chipshift-10));
+
+		map->fldrv_priv = newcfi;
+		*pcfi = newcfi;
+		kfree(cfi);
+	}
+
+	return 0;
+}
+
 /*
  *  *********** CHIP ACCESS FUNCTIONS ***********
  */
@@ -332,25 +619,87 @@
 {
 	DECLARE_WAITQUEUE(wait, current);
 	struct cfi_private *cfi = map->fldrv_priv;
-	cfi_word status, status_OK = CMD(0x80);
+	map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01);
 	unsigned long timeo;
-	struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv;
+	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
 
  resettime:
 	timeo = jiffies + HZ;
  retry:
+	if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {
+		/*
+		 * OK. We have possibility for contension on the write/erase
+		 * operations which are global to the real chip and not per
+		 * partition.  So let's fight it over in the partition which
+		 * currently has authority on the operation.
+		 *
+		 * The rules are as follows:
+		 *
+		 * - any write operation must own shared->writing.
+		 *
+		 * - any erase operation must own _both_ shared->writing and
+		 *   shared->erasing.
+		 *
+		 * - contension arbitration is handled in the owner's context.
+		 *
+		 * The 'shared' struct can be read when its lock is taken.
+		 * However any writes to it can only be made when the current
+		 * owner's lock is also held.
+		 */
+		struct flchip_shared *shared = chip->priv;
+		struct flchip *contender;
+		spin_lock(&shared->lock);
+		contender = shared->writing;
+		if (contender && contender != chip) {
+			/*
+			 * The engine to perform desired operation on this
+			 * partition is already in use by someone else.
+			 * Let's fight over it in the context of the chip
+			 * currently using it.  If it is possible to suspend,
+			 * that other partition will do just that, otherwise
+			 * it'll happily send us to sleep.  In any case, when
+			 * get_chip returns success we're clear to go ahead.
+			 */
+			int ret = spin_trylock(contender->mutex);
+			spin_unlock(&shared->lock);
+			if (!ret)
+				goto retry;
+			spin_unlock(chip->mutex);
+			ret = get_chip(map, contender, contender->start, mode);
+			spin_lock(chip->mutex);
+			if (ret) {
+				spin_unlock(contender->mutex);
+				return ret;
+			}
+			timeo = jiffies + HZ;
+			spin_lock(&shared->lock);
+		}
+
+		/* We now own it */
+		shared->writing = chip;
+		if (mode == FL_ERASING)
+			shared->erasing = chip;
+		if (contender && contender != chip)
+			spin_unlock(contender->mutex);
+		spin_unlock(&shared->lock);
+	}
+
 	switch (chip->state) {
 
 	case FL_STATUS:
 		for (;;) {
-			status = cfi_read(map, adr);
-			if ((status & status_OK) == status_OK)
+			status = map_read(map, adr);
+			if (map_word_andequal(map, status, status_OK, status_OK))
+				break;
+
+			/* At this point we're fine with write operations
+			   in other partitions as they don't conflict. */
+			if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS))
 				break;
 
 			if (time_after(jiffies, timeo)) {
-				printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n", 
-				       (long long)status);
-				spin_unlock(chip->mutex);
+				printk(KERN_ERR "%s: Waiting for chip to be ready timed out. Status %lx\n", 
+				       map->name, status.x[0]);
 				return -EIO;
 			}
 			spin_unlock(chip->mutex);
@@ -366,38 +715,39 @@
 		return 0;
 
 	case FL_ERASING:
-		if (!(cfip->FeatureSupport & 2) ||
+		if (!cfip ||
+		    !(cfip->FeatureSupport & 2) ||
 		    !(mode == FL_READY || mode == FL_POINT ||
 		     (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
 			goto sleep;
 
 
 		/* Erase suspend */
-		cfi_write(map, CMD(0xB0), adr);
+		map_write(map, CMD(0xB0), adr);
 
 		/* If the flash has finished erasing, then 'erase suspend'
 		 * appears to make some (28F320) flash devices switch to
 		 * 'read' mode.  Make sure that we switch to 'read status'
 		 * mode so we get the right data. --rmk
 		 */
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
 		chip->oldstate = FL_ERASING;
 		chip->state = FL_ERASE_SUSPENDING;
 		chip->erase_suspended = 1;
 		for (;;) {
-			status = cfi_read(map, adr);
-			if ((status & status_OK) == status_OK)
+			status = map_read(map, adr);
+			if (map_word_andequal(map, status, status_OK, status_OK))
 			        break;
 
 			if (time_after(jiffies, timeo)) {
 				/* Urgh. Resume and pretend we weren't here.  */
-				cfi_write(map, CMD(0xd0), adr);
+				map_write(map, CMD(0xd0), adr);
 				/* Make sure we're in 'read status' mode if it had finished */
-				cfi_write(map, CMD(0x70), adr);
+				map_write(map, CMD(0x70), adr);
 				chip->state = FL_ERASING;
 				chip->oldstate = FL_READY;
-				printk(KERN_ERR "Chip not ready after erase "
-				       "suspended: status = 0x%x\n", status);
+				printk(KERN_ERR "%s: Chip not ready after erase "
+				       "suspended: status = 0x%lx\n", map->name, status.x[0]);
 				return -EIO;
 			}
 
@@ -410,6 +760,14 @@
 		chip->state = FL_STATUS;
 		return 0;
 
+	case FL_XIP_WHILE_ERASING:
+		if (mode != FL_READY && mode != FL_POINT &&
+		    (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1)))
+			goto sleep;
+		chip->oldstate = chip->state;
+		chip->state = FL_READY;
+		return 0;
+
 	case FL_POINT:
 		/* Only if there's no operation suspended... */
 		if (mode == FL_READY && chip->oldstate == FL_READY)
@@ -431,6 +789,41 @@
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 
+	if (chip->priv) {
+		struct flchip_shared *shared = chip->priv;
+		spin_lock(&shared->lock);
+		if (shared->writing == chip && chip->oldstate == FL_READY) {
+			/* We own the ability to write, but we're done */
+			shared->writing = shared->erasing;
+			if (shared->writing && shared->writing != chip) {
+				/* give back ownership to who we loaned it from */
+				struct flchip *loaner = shared->writing;
+				spin_lock(loaner->mutex);
+				spin_unlock(&shared->lock);
+				spin_unlock(chip->mutex);
+				put_chip(map, loaner, loaner->start);
+				spin_lock(chip->mutex);
+				spin_unlock(loaner->mutex);
+				wake_up(&chip->wq);
+				return;
+			}
+			shared->erasing = NULL;
+			shared->writing = NULL;
+		} else if (shared->erasing == chip && shared->writing != chip) {
+			/*
+			 * We own the ability to erase without the ability
+			 * to write, which means the erase was suspended
+			 * and some other partition is currently writing.
+			 * Don't let the switch below mess things up since
+			 * we don't have ownership to resume anything.
+			 */
+			spin_unlock(&shared->lock);
+			wake_up(&chip->wq);
+			return;
+		}
+		spin_unlock(&shared->lock);
+	}
+
 	switch(chip->oldstate) {
 	case FL_ERASING:
 		chip->state = chip->oldstate;
@@ -443,12 +836,17 @@
 		   sending the 0x70 (Read Status) command to an erasing
 		   chip and expecting it to be ignored, that's what we 
 		   do. */
-		cfi_write(map, CMD(0xd0), adr);
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0xd0), adr);
+		map_write(map, CMD(0x70), adr);
 		chip->oldstate = FL_READY;
 		chip->state = FL_ERASING;
 		break;
 
+	case FL_XIP_WHILE_ERASING:
+		chip->state = chip->oldstate;
+		chip->oldstate = FL_READY;
+		break;
+
 	case FL_READY:
 	case FL_STATUS:
 	case FL_JEDEC_QUERY:
@@ -456,11 +854,214 @@
 		DISABLE_VPP(map);
 		break;
 	default:
-		printk(KERN_ERR "put_chip() called with oldstate %d!!\n", chip->oldstate);
+		printk(KERN_ERR "%s: put_chip() called with oldstate %d!!\n", map->name, chip->oldstate);
 	}
 	wake_up(&chip->wq);
 }
 
+#ifdef CONFIG_MTD_XIP
+
+/*
+ * No interrupt what so ever can be serviced while the flash isn't in array
+ * mode.  This is ensured by the xip_disable() and xip_enable() functions
+ * enclosing any code path where the flash is known not to be in array mode.
+ * And within a XIP disabled code path, only functions marked with __xipram
+ * may be called and nothing else (it's a good thing to inspect generated
+ * assembly to make sure inline functions were actually inlined and that gcc
+ * didn't emit calls to its own support functions). Also configuring MTD CFI
+ * support to a single buswidth and a single interleave is also recommended.
+ */
+
+static void xip_disable(struct map_info *map, struct flchip *chip,
+			unsigned long adr)
+{
+	/* TODO: chips with no XIP use should ignore and return */
+	(void) map_read(map, adr); /* ensure mmu mapping is up to date */
+	local_irq_disable();
+}
+
+static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
+				unsigned long adr)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	if (chip->state != FL_POINT && chip->state != FL_READY) {
+		map_write(map, CMD(0xff), adr);
+		chip->state = FL_READY;
+	}
+	(void) map_read(map, adr);
+	xip_iprefetch();
+	local_irq_enable();
+}
+
+/*
+ * When a delay is required for the flash operation to complete, the
+ * xip_udelay() function is polling for both the given timeout and pending
+ * (but still masked) hardware interrupts.  Whenever there is an interrupt
+ * pending then the flash erase or write operation is suspended, array mode
+ * restored and interrupts unmasked.  Task scheduling might also happen at that
+ * point.  The CPU eventually returns from the interrupt or the call to
+ * schedule() and the suspended flash operation is resumed for the remaining
+ * of the delay period.
+ *
+ * Warning: this function _will_ fool interrupt latency tracing tools.
+ */
+
+static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
+				unsigned long adr, int usec)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
+	map_word status, OK = CMD(0x80);
+	unsigned long suspended, start = xip_currtime();
+	flstate_t oldstate, newstate;
+
+	do {
+		cpu_relax();
+		if (xip_irqpending() && cfip &&
+		    ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) ||
+		     (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) &&
+		    (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
+			/*
+			 * Let's suspend the erase or write operation when
+			 * supported.  Note that we currently don't try to
+			 * suspend interleaved chips if there is already
+			 * another operation suspended (imagine what happens
+			 * when one chip was already done with the current
+			 * operation while another chip suspended it, then
+			 * we resume the whole thing at once).  Yes, it
+			 * can happen!
+			 */
+			map_write(map, CMD(0xb0), adr);
+			map_write(map, CMD(0x70), adr);
+			usec -= xip_elapsed_since(start);
+			suspended = xip_currtime();
+			do {
+				if (xip_elapsed_since(suspended) > 100000) {
+					/*
+					 * The chip doesn't want to suspend
+					 * after waiting for 100 msecs.
+					 * This is a critical error but there
+					 * is not much we can do here.
+					 */
+					return;
+				}
+				status = map_read(map, adr);
+			} while (!map_word_andequal(map, status, OK, OK));
+
+			/* Suspend succeeded */
+			oldstate = chip->state;
+			if (oldstate == FL_ERASING) {
+				if (!map_word_bitsset(map, status, CMD(0x40)))
+					break;
+				newstate = FL_XIP_WHILE_ERASING;
+				chip->erase_suspended = 1;
+			} else {
+				if (!map_word_bitsset(map, status, CMD(0x04)))
+					break;
+				newstate = FL_XIP_WHILE_WRITING;
+				chip->write_suspended = 1;
+			}
+			chip->state = newstate;
+			map_write(map, CMD(0xff), adr);
+			(void) map_read(map, adr);
+			asm volatile (".rep 8; nop; .endr");
+			local_irq_enable();
+			spin_unlock(chip->mutex);
+			asm volatile (".rep 8; nop; .endr");
+			cond_resched();
+
+			/*
+			 * We're back.  However someone else might have
+			 * decided to go write to the chip if we are in
+			 * a suspended erase state.  If so let's wait
+			 * until it's done.
+			 */
+			spin_lock(chip->mutex);
+			while (chip->state != newstate) {
+				DECLARE_WAITQUEUE(wait, current);
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				add_wait_queue(&chip->wq, &wait);
+				spin_unlock(chip->mutex);
+				schedule();
+				remove_wait_queue(&chip->wq, &wait);
+				spin_lock(chip->mutex);
+			}
+			/* Disallow XIP again */
+			local_irq_disable();
+
+			/* Resume the write or erase operation */
+			map_write(map, CMD(0xd0), adr);
+			map_write(map, CMD(0x70), adr);
+			chip->state = oldstate;
+			start = xip_currtime();
+		} else if (usec >= 1000000/HZ) {
+			/*
+			 * Try to save on CPU power when waiting delay
+			 * is at least a system timer tick period.
+			 * No need to be extremely accurate here.
+			 */
+			xip_cpu_idle();
+		}
+		status = map_read(map, adr);
+	} while (!map_word_andequal(map, status, OK, OK)
+		 && xip_elapsed_since(start) < usec);
+}
+
+#define UDELAY(map, chip, adr, usec)  xip_udelay(map, chip, adr, usec)
+
+/*
+ * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
+ * the flash is actively programming or erasing since we have to poll for
+ * the operation to complete anyway.  We can't do that in a generic way with
+ * a XIP setup so do it before the actual flash operation in this case
+ * and stub it out from INVALIDATE_CACHE_UDELAY.
+ */
+#define XIP_INVAL_CACHED_RANGE(map, from, size)  \
+	INVALIDATE_CACHED_RANGE(map, from, size)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
+	UDELAY(map, chip, adr, usec)
+
+/*
+ * Extra notes:
+ *
+ * Activating this XIP support changes the way the code works a bit.  For
+ * example the code to suspend the current process when concurrent access
+ * happens is never executed because xip_udelay() will always return with the
+ * same chip state as it was entered with.  This is why there is no care for
+ * the presence of add_wait_queue() or schedule() calls from within a couple
+ * xip_disable()'d  areas of code, like in do_erase_oneblock for example.
+ * The queueing and scheduling are always happening within xip_udelay().
+ *
+ * Similarly, get_chip() and put_chip() just happen to always be executed
+ * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
+ * is in array mode, therefore never executing many cases therein and not
+ * causing any problem with XIP.
+ */
+
+#else
+
+#define xip_disable(map, chip, adr)
+#define xip_enable(map, chip, adr)
+#define XIP_INVAL_CACHED_RANGE(x...)
+
+#define UDELAY(map, chip, adr, usec)  \
+do {  \
+	spin_unlock(chip->mutex);  \
+	cfi_udelay(usec);  \
+	spin_lock(chip->mutex);  \
+} while (0)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
+do {  \
+	spin_unlock(chip->mutex);  \
+	INVALIDATE_CACHED_RANGE(map, adr, len);  \
+	cfi_udelay(usec);  \
+	spin_lock(chip->mutex);  \
+} while (0)
+
+#endif
+
 static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
 {
 	unsigned long cmd_addr;
@@ -470,7 +1071,7 @@
 	adr += chip->start;
 
 	/* Ensure cmd read/writes are aligned. */ 
-	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
+	cmd_addr = adr & ~(map_bankwidth(map)-1); 
 
 	spin_lock(chip->mutex);
 
@@ -478,7 +1079,7 @@
 
 	if (!ret) {
 		if (chip->state != FL_POINT && chip->state != FL_READY)
-			cfi_write(map, CMD(0xff), cmd_addr);
+			map_write(map, CMD(0xff), cmd_addr);
 
 		chip->state = FL_POINT;
 		chip->ref_point_counter++;
@@ -564,7 +1165,7 @@
 			if(chip->ref_point_counter == 0)
 				chip->state = FL_READY;
 		} else
-			printk(KERN_ERR "Warning: unpoint called on non pointed region\n"); /* Should this give an error? */
+			printk(KERN_ERR "%s: Warning: unpoint called on non pointed region\n", map->name); /* Should this give an error? */
 
 		put_chip(map, chip, chip->start);
 		spin_unlock(chip->mutex);
@@ -584,7 +1185,7 @@
 	adr += chip->start;
 
 	/* Ensure cmd read/writes are aligned. */ 
-	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
+	cmd_addr = adr & ~(map_bankwidth(map)-1); 
 
 	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, cmd_addr, FL_READY);
@@ -594,7 +1195,7 @@
 	}
 
 	if (chip->state != FL_POINT && chip->state != FL_READY) {
-		cfi_write(map, CMD(0xff), cmd_addr);
+		map_write(map, CMD(0xff), cmd_addr);
 
 		chip->state = FL_READY;
 	}
@@ -646,130 +1247,51 @@
 	return ret;
 }
 
-static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz)
-{
-	struct map_info *map = mtd->priv;
-	struct cfi_private *cfi = map->fldrv_priv;
-	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
-	struct flchip *chip;
-	int ofs_factor = cfi->interleave * cfi->device_type;
-	int count = len;
-	int chip_num, offst;
-	int ret;
-
-	chip_num = ((unsigned int)from/reg_sz);
-	offst = from - (reg_sz*chip_num)+base_offst;
-
-	while (count) {
-	/* Calculate which chip & protection register offset we need */
-
-		if (chip_num >= cfi->numchips)
-			goto out;
-
-		chip = &cfi->chips[chip_num];
-		
-		spin_lock(chip->mutex);
-		ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
-		if (ret) {
-			spin_unlock(chip->mutex);
-			return (len-count)?:ret;
-		}
-
-		if (chip->state != FL_JEDEC_QUERY) {
-			cfi_write(map, CMD(0x90), chip->start);
-			chip->state = FL_JEDEC_QUERY;
-		}
-
-		while (count && ((offst-base_offst) < reg_sz)) {
-			*buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
-			buf++;
-			offst++;
-			count--;
-		}
-
-		put_chip(map, chip, chip->start);
-		spin_unlock(chip->mutex);
-
-		/* Move on to the next chip */
-		chip_num++;
-		offst = base_offst;
-	}
-	
- out:	
-	return len-count;
-}
-	
-static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
-	struct map_info *map = mtd->priv;
-	struct cfi_private *cfi = map->fldrv_priv;
-	struct cfi_pri_intelext *extp=cfi->cmdset_priv;
-	int base_offst,reg_sz;
-	
-	/* Check that we actually have some protection registers */
-	if(!(extp->FeatureSupport&64)){
-		printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
-		return 0;
-	}
-
-	base_offst=(1<<extp->FactProtRegSize);
-	reg_sz=(1<<extp->UserProtRegSize);
-
-	return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
-}
-
-static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
-	struct map_info *map = mtd->priv;
-	struct cfi_private *cfi = map->fldrv_priv;
-	struct cfi_pri_intelext *extp=cfi->cmdset_priv;
-	int base_offst,reg_sz;
-	
-	/* Check that we actually have some protection registers */
-	if(!(extp->FeatureSupport&64)){
-		printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
-		return 0;
-	}
-
-	base_offst=0;
-	reg_sz=(1<<extp->FactProtRegSize);
-
-	return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
-}
-
-
-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
+static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
+				     unsigned long adr, map_word datum, int mode)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	cfi_word status, status_OK;
+	map_word status, status_OK, write_cmd;
 	unsigned long timeo;
 	int z, ret=0;
 
 	adr += chip->start;
 
-	/* Let's determine this according to the interleave only once */
+	/* Let's determine those according to the interleave only once */
 	status_OK = CMD(0x80);
+	switch (mode) {
+	case FL_WRITING:
+		write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41);
+		break;
+	case FL_OTP_WRITE:
+		write_cmd = CMD(0xc0);
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	spin_lock(chip->mutex);
-	ret = get_chip(map, chip, adr, FL_WRITING);
+	ret = get_chip(map, chip, adr, mode);
 	if (ret) {
 		spin_unlock(chip->mutex);
 		return ret;
 	}
 
+	XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
 	ENABLE_VPP(map);
-	cfi_write(map, CMD(0x40), adr);
-	cfi_write(map, datum, adr);
-	chip->state = FL_WRITING;
-
-	spin_unlock(chip->mutex);
-	cfi_udelay(chip->word_write_time);
-	spin_lock(chip->mutex);
+	xip_disable(map, chip, adr);
+	map_write(map, write_cmd, adr);
+	map_write(map, datum, adr);
+	chip->state = mode;
+
+	INVALIDATE_CACHE_UDELAY(map, chip,
+				adr, map_bankwidth(map),
+				chip->word_write_time);
 
 	timeo = jiffies + (HZ/2);
 	z = 0;
 	for (;;) {
-		if (chip->state != FL_WRITING) {
+		if (chip->state != mode) {
 			/* Someone's suspended the write. Sleep */
 			DECLARE_WAITQUEUE(wait, current);
 
@@ -783,46 +1305,60 @@
 			continue;
 		}
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for chip to be ready timed out in word write\n");
+			xip_enable(map, chip, adr);
+			printk(KERN_ERR "%s: word write error (status timeout)\n", map->name);
 			ret = -EIO;
 			goto out;
 		}
 
 		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
 		z++;
-		cfi_udelay(1);
-		spin_lock(chip->mutex);
+		UDELAY(map, chip, adr, 1);
 	}
 	if (!z) {
 		chip->word_write_time--;
 		if (!chip->word_write_time)
-			chip->word_write_time++;
+			chip->word_write_time = 1;
 	}
 	if (z > 1) 
 		chip->word_write_time++;
 
 	/* Done and happy. */
 	chip->state = FL_STATUS;
-	/* check for lock bit */
-	if (status & CMD(0x02)) {
-		/* clear status */
-		cfi_write(map, CMD(0x50), adr);
-		/* put back into read status register mode */
-		cfi_write(map, CMD(0x70), adr);
-		ret = -EROFS;
+
+	/* check for errors */
+	if (map_word_bitsset(map, status, CMD(0x1a))) {
+		unsigned long chipstatus = MERGESTATUS(status);
+
+		/* reset status */
+		map_write(map, CMD(0x50), adr);
+		map_write(map, CMD(0x70), adr);
+		xip_enable(map, chip, adr);
+
+		if (chipstatus & 0x02) {
+			ret = -EROFS;
+		} else if (chipstatus & 0x08) {
+			printk(KERN_ERR "%s: word write error (bad VPP)\n", map->name);
+			ret = -EIO;
+		} else {
+			printk(KERN_ERR "%s: word write error (status 0x%lx)\n", map->name, chipstatus);
+			ret = -EINVAL;
+		}
+
+		goto out;
 	}
- out:
-	put_chip(map, chip, adr);
-	spin_unlock(chip->mutex);
 
+	xip_enable(map, chip, adr);
+ out:	put_chip(map, chip, adr);
+	spin_unlock(chip->mutex);
 	return ret;
 }
 
@@ -843,35 +1379,22 @@
 	ofs = to  - (chipnum << cfi->chipshift);
 
 	/* If it's not bus-aligned, do the first byte write */
-	if (ofs & (CFIDEV_BUSWIDTH-1)) {
-		unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
+	if (ofs & (map_bankwidth(map)-1)) {
+		unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1);
 		int gap = ofs - bus_ofs;
-		int i = 0, n = 0;
-		u_char tmp_buf[8];
-		cfi_word datum;
-
-		while (gap--)
-			tmp_buf[i++] = 0xff;
-		while (len && i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = buf[n++], len--;
-		while (i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = 0xff;
-
-		if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)tmp_buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)tmp_buf;
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)tmp_buf;
-		} else {
-			return -EINVAL;  /* should never happen, but be safe */
-		}
+		int n;
+		map_word datum;
+
+		n = min_t(int, len, map_bankwidth(map)-gap);
+		datum = map_word_ff(map);
+		datum = map_word_load_partial(map, datum, buf, gap, n);
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum],
-					       bus_ofs, datum);
+					       bus_ofs, datum, FL_WRITING);
 		if (ret) 
 			return ret;
-		
+
+		len -= n;
 		ofs += n;
 		buf += n;
 		(*retlen) += n;
@@ -884,30 +1407,18 @@
 		}
 	}
 	
-	while(len >= CFIDEV_BUSWIDTH) {
-		cfi_word datum;
-
-		if (cfi_buswidth_is_1()) {
-			datum = *(__u8*)buf;
-		} else if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)buf;
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)buf;
-		} else {
-			return -EINVAL;
-		}
+	while(len >= map_bankwidth(map)) {
+		map_word datum = map_word_load(map, buf);
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum],
-				ofs, datum);
+				       ofs, datum, FL_WRITING);
 		if (ret)
 			return ret;
 
-		ofs += CFIDEV_BUSWIDTH;
-		buf += CFIDEV_BUSWIDTH;
-		(*retlen) += CFIDEV_BUSWIDTH;
-		len -= CFIDEV_BUSWIDTH;
+		ofs += map_bankwidth(map);
+		buf += map_bankwidth(map);
+		(*retlen) += map_bankwidth(map);
+		len -= map_bankwidth(map);
 
 		if (ofs >> cfi->chipshift) {
 			chipnum ++; 
@@ -917,52 +1428,42 @@
 		}
 	}
 
-	if (len & (CFIDEV_BUSWIDTH-1)) {
-		int i = 0, n = 0;
-		u_char tmp_buf[8];
-		cfi_word datum;
-
-		while (len--)
-			tmp_buf[i++] = buf[n++];
-		while (i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = 0xff;
-
-		if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)tmp_buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)tmp_buf;
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)tmp_buf;
-		} else {
-			return -EINVAL;  /* should never happen, but be safe */
-		}
+	if (len & (map_bankwidth(map)-1)) {
+		map_word datum;
+
+		datum = map_word_ff(map);
+		datum = map_word_load_partial(map, datum, buf, 0, len);
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum],
-					       ofs, datum);
+				       ofs, datum, FL_WRITING);
 		if (ret) 
 			return ret;
 		
-		(*retlen) += n;
+		(*retlen) += len;
 	}
 
 	return 0;
 }
 
 
-static inline int do_write_buffer(struct map_info *map, struct flchip *chip, 
-				  unsigned long adr, const u_char *buf, int len)
+static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, 
+				    unsigned long adr, const struct kvec **pvec,
+				    unsigned long *pvec_seek, int len)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	cfi_word status, status_OK;
+	map_word status, status_OK, write_cmd, datum;
 	unsigned long cmd_adr, timeo;
-	int wbufsize, z, ret=0, bytes, words;
+	int wbufsize, z, ret=0, word_gap, words;
+	const struct kvec *vec;
+	unsigned long vec_seek;
 
-	wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+	wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
 	adr += chip->start;
 	cmd_adr = adr & ~(wbufsize-1);
-	
+
 	/* Let's determine this according to the interleave only once */
 	status_OK = CMD(0x80);
+	write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9);
 
 	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, cmd_adr, FL_WRITING);
@@ -971,96 +1472,109 @@
 		return ret;
 	}
 
-	if (chip->state != FL_STATUS)
-		cfi_write(map, CMD(0x70), cmd_adr);
-
-	status = cfi_read(map, cmd_adr);
+	XIP_INVAL_CACHED_RANGE(map, adr, len);
+	ENABLE_VPP(map);
+	xip_disable(map, chip, cmd_adr);
 
 	/* �4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
 	   [...], the device will not accept any more Write to Buffer commands". 
 	   So we must check here and reset those bits if they're set. Otherwise
 	   we're just pissing in the wind */
-	if (status & CMD(0x30)) {
-		printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %x). Clearing.\n", status);
-		cfi_write(map, CMD(0x50), cmd_adr);
-		cfi_write(map, CMD(0x70), cmd_adr);
+	if (chip->state != FL_STATUS)
+		map_write(map, CMD(0x70), cmd_adr);
+	status = map_read(map, cmd_adr);
+	if (map_word_bitsset(map, status, CMD(0x30))) {
+		xip_enable(map, chip, cmd_adr);
+		printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]);
+		xip_disable(map, chip, cmd_adr);
+		map_write(map, CMD(0x50), cmd_adr);
+		map_write(map, CMD(0x70), cmd_adr);
 	}
-	ENABLE_VPP(map);
+
 	chip->state = FL_WRITING_TO_BUFFER;
 
 	z = 0;
 	for (;;) {
-		cfi_write(map, CMD(0xe8), cmd_adr);
+		map_write(map, write_cmd, cmd_adr);
 
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 
-		spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		spin_lock(chip->mutex);
+		UDELAY(map, chip, cmd_adr, 1);
 
 		if (++z > 20) {
 			/* Argh. Not ready for write to buffer */
-			cfi_write(map, CMD(0x70), cmd_adr);
+			map_word Xstatus;
+			map_write(map, CMD(0x70), cmd_adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr));
+			Xstatus = map_read(map, cmd_adr);
 			/* Odd. Clear status bits */
-			cfi_write(map, CMD(0x50), cmd_adr);
-			cfi_write(map, CMD(0x70), cmd_adr);
+			map_write(map, CMD(0x50), cmd_adr);
+			map_write(map, CMD(0x70), cmd_adr);
+			xip_enable(map, chip, cmd_adr);
+			printk(KERN_ERR "%s: Chip not ready for buffer write. status = %lx, Xstatus = %lx\n",
+			       map->name, status.x[0], Xstatus.x[0]);
 			ret = -EIO;
 			goto out;
 		}
 	}
 
+	/* Figure out the number of words to write */
+	word_gap = (-adr & (map_bankwidth(map)-1));
+	words = (len - word_gap + map_bankwidth(map) - 1) / map_bankwidth(map);
+	if (!word_gap) {
+		words--;
+	} else {
+		word_gap = map_bankwidth(map) - word_gap;
+		adr -= word_gap;
+		datum = map_word_ff(map);
+	}
+
 	/* Write length of data to come */
-	bytes = len & (CFIDEV_BUSWIDTH-1);
-	words = len / CFIDEV_BUSWIDTH;
-	cfi_write(map, CMD(words - !bytes), cmd_adr );
+	map_write(map, CMD(words), cmd_adr );
 
 	/* Write data */
-	z = 0;
-	while(z < words * CFIDEV_BUSWIDTH) {
-		if (cfi_buswidth_is_1()) {
-			map_write8 (map, *((__u8*)buf)++, adr+z);
-		} else if (cfi_buswidth_is_2()) {
-			map_write16 (map, *((__u16*)buf)++, adr+z);
-		} else if (cfi_buswidth_is_4()) {
-			map_write32 (map, *((__u32*)buf)++, adr+z);
-		} else if (cfi_buswidth_is_8()) {
-			map_write64 (map, *((__u64*)buf)++, adr+z);
-		} else {
-			ret = -EINVAL;
-			goto out;
-		}
-		z += CFIDEV_BUSWIDTH;
-	}
-	if (bytes) {
-		int i = 0, n = 0;
-		u_char tmp_buf[8], *tmp_p = tmp_buf;
-
-		while (bytes--)
-			tmp_buf[i++] = buf[n++];
-		while (i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = 0xff;
-		if (cfi_buswidth_is_2()) {
-			map_write16 (map, *((__u16*)tmp_p)++, adr+z);
-		} else if (cfi_buswidth_is_4()) {
-			map_write32 (map, *((__u32*)tmp_p)++, adr+z);
-		} else if (cfi_buswidth_is_8()) {
-			map_write64 (map, *((__u64*)tmp_p)++, adr+z);
-		} else {
-			ret = -EINVAL;
-			goto out;
-		}
-	}
+	vec = *pvec;
+	vec_seek = *pvec_seek;
+	do {
+		int n = map_bankwidth(map) - word_gap;
+		if (n > vec->iov_len - vec_seek)
+			n = vec->iov_len - vec_seek;
+		if (n > len)
+			n = len;
+
+		if (!word_gap && len < map_bankwidth(map))
+			datum = map_word_ff(map);
+			
+		datum = map_word_load_partial(map, datum,
+					      vec->iov_base + vec_seek, 
+					      word_gap, n);
+
+		len -= n;
+		word_gap += n;
+		if (!len || word_gap == map_bankwidth(map)) {
+			map_write(map, datum, adr);
+			adr += map_bankwidth(map);
+			word_gap = 0;
+		}
+
+		vec_seek += n;
+		if (vec_seek == vec->iov_len) {
+			vec++;
+			vec_seek = 0;
+		}
+	} while (len);
+	*pvec = vec;
+	*pvec_seek = vec_seek;
+
 	/* GO GO GO */
-	cfi_write(map, CMD(0xd0), cmd_adr);
+	map_write(map, CMD(0xd0), cmd_adr);
 	chip->state = FL_WRITING;
 
-	spin_unlock(chip->mutex);
-	cfi_udelay(chip->buffer_write_time);
-	spin_lock(chip->mutex);
+	INVALIDATE_CACHE_UDELAY(map, chip, 
+				cmd_adr, len,
+				chip->buffer_write_time);
 
 	timeo = jiffies + (HZ/2);
 	z = 0;
@@ -1078,28 +1592,28 @@
 			continue;
 		}
 
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
+			map_write(map, CMD(0x70), cmd_adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n");
+			xip_enable(map, chip, cmd_adr);
+			printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name);
 			ret = -EIO;
 			goto out;
 		}
 		
 		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
-		cfi_udelay(1);
 		z++;
-		spin_lock(chip->mutex);
+		UDELAY(map, chip, cmd_adr, 1);
 	}
 	if (!z) {
 		chip->buffer_write_time--;
 		if (!chip->buffer_write_time)
-			chip->buffer_write_time++;
+			chip->buffer_write_time = 1;
 	}
 	if (z > 1) 
 		chip->buffer_write_time++;
@@ -1107,73 +1621,68 @@
 	/* Done and happy. */
  	chip->state = FL_STATUS;
 
-	/* check for lock bit */
-	if (status & CMD(0x02)) {
-		/* clear status */
-		cfi_write(map, CMD(0x50), cmd_adr);
-		/* put back into read status register mode */
-		cfi_write(map, CMD(0x70), adr);
-		ret = -EROFS;
+	/* check for errors */
+	if (map_word_bitsset(map, status, CMD(0x1a))) {
+		unsigned long chipstatus = MERGESTATUS(status);
+
+		/* reset status */
+		map_write(map, CMD(0x50), cmd_adr);
+		map_write(map, CMD(0x70), cmd_adr);
+		xip_enable(map, chip, cmd_adr);
+
+		if (chipstatus & 0x02) {
+			ret = -EROFS;
+		} else if (chipstatus & 0x08) {
+			printk(KERN_ERR "%s: buffer write error (bad VPP)\n", map->name);
+			ret = -EIO;
+		} else {
+			printk(KERN_ERR "%s: buffer write error (status 0x%lx)\n", map->name, chipstatus);
+			ret = -EINVAL;
+		}
+
+		goto out;
 	}
 
- out:
-	put_chip(map, chip, cmd_adr);
+	xip_enable(map, chip, cmd_adr);
+ out:	put_chip(map, chip, cmd_adr);
 	spin_unlock(chip->mutex);
 	return ret;
 }
 
-static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, 
-				       size_t len, size_t *retlen, const u_char *buf)
+static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,
+				unsigned long count, loff_t to, size_t *retlen)
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
 	int ret = 0;
 	int chipnum;
-	unsigned long ofs;
+	unsigned long ofs, vec_seek, i;
+	size_t len = 0;
+
+	for (i = 0; i < count; i++)
+		len += vecs[i].iov_len;
 
 	*retlen = 0;
 	if (!len)
 		return 0;
 
 	chipnum = to >> cfi->chipshift;
-	ofs = to  - (chipnum << cfi->chipshift);
+	ofs = to - (chipnum << cfi->chipshift);
+	vec_seek = 0;
 
-	/* If it's not bus-aligned, do the first word write */
-	if (ofs & (CFIDEV_BUSWIDTH-1)) {
-		size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1);
-		if (local_len > len)
-			local_len = len;
-		ret = cfi_intelext_write_words(mtd, to, local_len,
-					       retlen, buf);
-		if (ret)
-			return ret;
-		ofs += local_len;
-		buf += local_len;
-		len -= local_len;
-
-		if (ofs >> cfi->chipshift) {
-			chipnum ++;
-			ofs = 0;
-			if (chipnum == cfi->numchips)
-				return 0;
-		}
-	}
-
-	/* Write buffer is worth it only if more than one word to write... */
-	while(len) {
+	do {
 		/* We must not cross write block boundaries */
 		int size = wbufsize - (ofs & (wbufsize-1));
 
 		if (size > len)
 			size = len;
 		ret = do_write_buffer(map, &cfi->chips[chipnum], 
-				      ofs, buf, size);
+				      ofs, &vecs, &vec_seek, size);
 		if (ret)
 			return ret;
 
 		ofs += size;
-		buf += size;
 		(*retlen) += size;
 		len -= size;
 
@@ -1183,106 +1692,27 @@
 			if (chipnum == cfi->numchips)
 				return 0;
 		}
-	}
+	} while (len);
+
 	return 0;
 }
 
-typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
-			      unsigned long adr, void *thunk);
-
-static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
-				     loff_t ofs, size_t len, void *thunk)
+static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to,
+				       size_t len, size_t *retlen, const u_char *buf)
 {
-	struct map_info *map = mtd->priv;
-	struct cfi_private *cfi = map->fldrv_priv;
-	unsigned long adr;
-	int chipnum, ret = 0;
-	int i, first;
-	struct mtd_erase_region_info *regions = mtd->eraseregions;
-
-	if (ofs > mtd->size)
-		return -EINVAL;
-
-	if ((len + ofs) > 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 && ofs >= 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 (ofs & (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 && (ofs + len) >= regions[i].offset)
-		i++;
-
-	/* As before, drop back one to point at the region in which
-	   the address actually falls
-	*/
-	i--;
-	
-	if ((ofs + len) & (regions[i].erasesize-1))
-		return -EINVAL;
-
-	chipnum = ofs >> cfi->chipshift;
-	adr = ofs - (chipnum << cfi->chipshift);
+	struct kvec vec;
 
-	i=first;
+	vec.iov_base = (void *) buf;
+	vec.iov_len = len;
 
-	while(len) {
-		ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
-		
-		if (ret)
-			return ret;
-
-		adr += regions[i].erasesize;
-		len -= regions[i].erasesize;
-
-		if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
-			i++;
-
-		if (adr >> cfi->chipshift) {
-			adr = 0;
-			chipnum++;
-			
-			if (chipnum >= cfi->numchips)
-			break;
-		}
-	}
-
-	return 0;
+	return cfi_intelext_writev(mtd, &vec, 1, to, retlen);
 }
 
-
-static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
+				      unsigned long adr, int len, void *thunk)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	cfi_word status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo;
 	int retries = 3;
 	DECLARE_WAITQUEUE(wait, current);
@@ -1301,20 +1731,22 @@
 		return ret;
 	}
 
+	XIP_INVAL_CACHED_RANGE(map, adr, len);
 	ENABLE_VPP(map);
+	xip_disable(map, chip, adr);
+
 	/* Clear the status register first */
-	cfi_write(map, CMD(0x50), adr);
+	map_write(map, CMD(0x50), adr);
 
 	/* Now erase */
-	cfi_write(map, CMD(0x20), adr);
-	cfi_write(map, CMD(0xD0), adr);
+	map_write(map, CMD(0x20), adr);
+	map_write(map, CMD(0xD0), adr);
 	chip->state = FL_ERASING;
 	chip->erase_suspended = 0;
 
-	spin_unlock(chip->mutex);
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout((chip->erase_time*HZ)/(2*1000));
-	spin_lock(chip->mutex);
+	INVALIDATE_CACHE_UDELAY(map, chip,
+				adr, len,
+				chip->erase_time*1000/2);
 
 	/* FIXME. Use a timer to check this, and return immediately. */
 	/* Once the state machine's known to be working I'll do that */
@@ -1338,77 +1770,64 @@
 			chip->erase_suspended = 0;
 		}
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %llx, status = %llx.\n",
-			       adr, (__u64)status, (__u64)cfi_read(map, adr));
-			/* Clear status bits */
-			cfi_write(map, CMD(0x50), adr);
-			cfi_write(map, CMD(0x70), adr);
-			DISABLE_VPP(map);
-			spin_unlock(chip->mutex);
-			return -EIO;
+			xip_enable(map, chip, adr);
+			printk(KERN_ERR "%s: block erase error: (status timeout)\n", map->name);
+			ret = -EIO;
+			goto out;
 		}
 		
 		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-		spin_lock(chip->mutex);
+		UDELAY(map, chip, adr, 1000000/HZ);
 	}
-	
-	DISABLE_VPP(map);
-	ret = 0;
 
 	/* We've broken this before. It doesn't hurt to be safe */
-	cfi_write(map, CMD(0x70), adr);
+	map_write(map, CMD(0x70), adr);
 	chip->state = FL_STATUS;
-	status = cfi_read(map, adr);
+	status = map_read(map, adr);
+
+	/* check for errors */
+	if (map_word_bitsset(map, status, CMD(0x3a))) {
+		unsigned long chipstatus = MERGESTATUS(status);
 
-	/* check for lock bit */
-	if (status & CMD(0x3a)) {
-		unsigned char chipstatus = status;
-		if (status != CMD(status & 0xff)) {
-			int i;
-			for (i = 1; i<CFIDEV_INTERLEAVE; i++) {
-				      chipstatus |= status >> (cfi->device_type * 8);
-			}
-			printk(KERN_WARNING "Status is not identical for all chips: 0x%llx. Merging to give 0x%02x\n", (__u64)status, chipstatus);
-		}
 		/* Reset the error bits */
-		cfi_write(map, CMD(0x50), adr);
-		cfi_write(map, CMD(0x70), adr);
-		
+		map_write(map, CMD(0x50), adr);
+		map_write(map, CMD(0x70), adr);
+		xip_enable(map, chip, adr);
+
 		if ((chipstatus & 0x30) == 0x30) {
-			printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%llx\n", (__u64)status);
-			ret = -EIO;
+			printk(KERN_ERR "%s: block erase error: (bad command sequence, status 0x%lx)\n", map->name, chipstatus);
+			ret = -EINVAL;
 		} else if (chipstatus & 0x02) {
 			/* Protection bit set */
 			ret = -EROFS;
 		} else if (chipstatus & 0x8) {
 			/* Voltage */
-			printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%llx\n", (__u64)status);
+			printk(KERN_ERR "%s: block erase error: (bad VPP)\n", map->name);
 			ret = -EIO;
-		} else if (chipstatus & 0x20) {
-			if (retries--) {
-				printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx. Retrying...\n", adr, (__u64)status);
-				timeo = jiffies + HZ;
-				chip->state = FL_STATUS;
-				spin_unlock(chip->mutex);
-				goto retry;
-			}
-			printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx\n", adr, (__u64)status);
+		} else if (chipstatus & 0x20 && retries--) {
+			printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
+			timeo = jiffies + HZ;
+			put_chip(map, chip, adr);
+			spin_unlock(chip->mutex);
+			goto retry;
+		} else {
+			printk(KERN_ERR "%s: block erase failed at 0x%08lx (status 0x%lx)\n", map->name, adr, chipstatus);
 			ret = -EIO;
 		}
+
+		goto out;
 	}
 
-	wake_up(&chip->wq);
+	xip_enable(map, chip, adr);
+ out:	put_chip(map, chip, adr);
 	spin_unlock(chip->mutex);
 	return ret;
 }
@@ -1421,13 +1840,12 @@
 	ofs = instr->addr;
 	len = instr->len;
 
-	ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0);
+	ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
 	if (ret)
 		return ret;
 
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
@@ -1466,6 +1884,7 @@
 		
 		if (chip->state == FL_SYNCING) {
 			chip->state = chip->oldstate;
+			chip->oldstate = FL_READY;
 			wake_up(&chip->wq);
 		}
 		spin_unlock(chip->mutex);
@@ -1473,15 +1892,22 @@
 }
 
 #ifdef DEBUG_LOCK_BITS
-static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int __xipram do_printlockstatus_oneblock(struct map_info *map,
+						struct flchip *chip,
+						unsigned long adr,
+						int len, void *thunk)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	int ofs_factor = cfi->interleave * cfi->device_type;
+	int status, ofs_factor = cfi->interleave * cfi->device_type;
 
-	cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
-	printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
-	       adr, cfi_read_query(map, adr+(2*ofs_factor)));
+	adr += chip->start;
+	xip_disable(map, chip, adr+(2*ofs_factor));
+	map_write(map, CMD(0x90), adr+(2*ofs_factor));
 	chip->state = FL_JEDEC_QUERY;
+	status = cfi_read_query(map, adr+(2*ofs_factor));
+	xip_enable(map, chip, 0);
+	printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
+	       adr, status);
 	return 0;
 }
 #endif
@@ -1489,10 +1915,12 @@
 #define DO_XXLOCK_ONEBLOCK_LOCK		((void *) 1)
 #define DO_XXLOCK_ONEBLOCK_UNLOCK	((void *) 2)
 
-static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
+				       unsigned long adr, int len, void *thunk)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	cfi_word status, status_OK;
+	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+	map_word status, status_OK;
 	unsigned long timeo = jiffies + HZ;
 	int ret;
 
@@ -1509,20 +1937,25 @@
 	}
 
 	ENABLE_VPP(map);
-	cfi_write(map, CMD(0x60), adr);
-
+	xip_disable(map, chip, adr);
+	
+	map_write(map, CMD(0x60), adr);
 	if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) {
-		cfi_write(map, CMD(0x01), adr);
+		map_write(map, CMD(0x01), adr);
 		chip->state = FL_LOCKING;
 	} else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) {
-		cfi_write(map, CMD(0xD0), adr);
+		map_write(map, CMD(0xD0), adr);
 		chip->state = FL_UNLOCKING;
 	} else
 		BUG();
 
-	spin_unlock(chip->mutex);
-	schedule_timeout(HZ);
-	spin_lock(chip->mutex);
+	/*
+	 * If Instant Individual Block Locking supported then no need
+	 * to delay.
+	 */
+
+	if (!extp || !(extp->FeatureSupport & (1 << 5)))
+		UDELAY(map, chip, adr, 1000000/HZ);
 
 	/* FIXME. Use a timer to check this, and return immediately. */
 	/* Once the state machine's known to be working I'll do that */
@@ -1530,28 +1963,28 @@
 	timeo = jiffies + (HZ*20);
 	for (;;) {
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %llx, status = %llx.\n", (__u64)status, (__u64)cfi_read(map, adr));
-			DISABLE_VPP(map);
+			xip_enable(map, chip, adr);
+			printk(KERN_ERR "%s: block unlock error: (status timeout)\n", map->name);
+			put_chip(map, chip, adr);
 			spin_unlock(chip->mutex);
 			return -EIO;
 		}
 		
 		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		spin_lock(chip->mutex);
+		UDELAY(map, chip, adr, 1);
 	}
 	
 	/* Done and happy. */
 	chip->state = FL_STATUS;
+	xip_enable(map, chip, adr);
 	put_chip(map, chip, adr);
 	spin_unlock(chip->mutex);
 	return 0;
@@ -1564,18 +1997,18 @@
 #ifdef DEBUG_LOCK_BITS
 	printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
 	       __FUNCTION__, ofs, len);
-	cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
-				  ofs, len, 0);
+	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
+		ofs, len, 0);
 #endif
 
-	ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, 
-					ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);
+	ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, 
+		ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);
 	
 #ifdef DEBUG_LOCK_BITS
-	printk(KERN_DEBUG __FUNCTION__
-	       "%s: lock status after, ret=%d\n", __FUNCTION__, ret);
-	cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
-				  ofs, len, 0);
+	printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
+	       __FUNCTION__, ret);
+	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
+		ofs, len, 0);
 #endif
 
 	return ret;
@@ -1588,22 +2021,291 @@
 #ifdef DEBUG_LOCK_BITS
 	printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
 	       __FUNCTION__, ofs, len);
-	cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
-				  ofs, len, 0);
+	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
+		ofs, len, 0);
 #endif
 
-	ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock,
+	ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
 					ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);
 	
 #ifdef DEBUG_LOCK_BITS
-	printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret);
-	cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, 
-				  ofs, len, 0);
+	printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
+	       __FUNCTION__, ret);
+	cfi_varsize_frob(mtd, do_printlockstatus_oneblock, 
+		ofs, len, 0);
 #endif
 	
 	return ret;
 }
 
+#ifdef CONFIG_MTD_OTP
+
+typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, 
+			u_long data_offset, u_char *buf, u_int size,
+			u_long prot_offset, u_int groupno, u_int groupsize);
+
+static int __xipram
+do_otp_read(struct map_info *map, struct flchip *chip, u_long offset,
+	    u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	int ret;
+
+	spin_lock(chip->mutex);
+	ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
+	if (ret) {
+		spin_unlock(chip->mutex);
+		return ret;
+	}
+
+	/* let's ensure we're not reading back cached data from array mode */
+	INVALIDATE_CACHED_RANGE(map, chip->start + offset, size);
+
+	xip_disable(map, chip, chip->start);
+	if (chip->state != FL_JEDEC_QUERY) {
+		map_write(map, CMD(0x90), chip->start);
+		chip->state = FL_JEDEC_QUERY;
+	}
+	map_copy_from(map, buf, chip->start + offset, size);
+	xip_enable(map, chip, chip->start);
+
+	/* then ensure we don't keep OTP data in the cache */
+	INVALIDATE_CACHED_RANGE(map, chip->start + offset, size);
+
+	put_chip(map, chip, chip->start);
+	spin_unlock(chip->mutex);
+	return 0;
+}
+
+static int
+do_otp_write(struct map_info *map, struct flchip *chip, u_long offset,
+	     u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
+{
+	int ret;
+
+	while (size) {
+		unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1);
+		int gap = offset - bus_ofs;
+		int n = min_t(int, size, map_bankwidth(map)-gap);
+		map_word datum = map_word_ff(map);
+
+		datum = map_word_load_partial(map, datum, buf, gap, n);
+		ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE);
+		if (ret) 
+			return ret;
+
+		offset += n;
+		buf += n;
+		size -= n;
+	}
+
+	return 0;
+}
+
+static int
+do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset,
+	    u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	map_word datum;
+
+	/* make sure area matches group boundaries */
+	if (size != grpsz)
+		return -EXDEV;
+
+	datum = map_word_ff(map);
+	datum = map_word_clr(map, datum, CMD(1 << grpno));
+	return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE);
+}
+
+static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
+				 size_t *retlen, u_char *buf,
+				 otp_op_t action, int user_regs)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+	struct flchip *chip;
+	struct cfi_intelext_otpinfo *otp;
+	u_long devsize, reg_prot_offset, data_offset;
+	u_int chip_num, chip_step, field, reg_fact_size, reg_user_size;
+	u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups;
+	int ret;
+
+	*retlen = 0;
+
+	/* Check that we actually have some OTP registers */
+	if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields)
+		return -ENODATA;
+
+	/* we need real chips here not virtual ones */
+	devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave;
+	chip_step = devsize >> cfi->chipshift;
+	chip_num = 0;
+
+	/* Some chips have OTP located in the _top_ partition only.
+	   For example: Intel 28F256L18T (T means top-parameter device) */
+	if (cfi->mfr == MANUFACTURER_INTEL) {
+		switch (cfi->id) {
+		case 0x880b:
+		case 0x880c:
+		case 0x880d:
+			chip_num = chip_step - 1;
+		}
+	}
+
+	for ( ; chip_num < cfi->numchips; chip_num += chip_step) {
+		chip = &cfi->chips[chip_num];
+		otp = (struct cfi_intelext_otpinfo *)&extp->extra[0];
+
+		/* first OTP region */
+		field = 0;
+		reg_prot_offset = extp->ProtRegAddr;
+		reg_fact_groups = 1;
+		reg_fact_size = 1 << extp->FactProtRegSize;
+		reg_user_groups = 1;
+		reg_user_size = 1 << extp->UserProtRegSize;
+
+		while (len > 0) {
+			/* flash geometry fixup */
+			data_offset = reg_prot_offset + 1;
+			data_offset *= cfi->interleave * cfi->device_type;
+			reg_prot_offset *= cfi->interleave * cfi->device_type;
+			reg_fact_size *= cfi->interleave;
+			reg_user_size *= cfi->interleave;
+
+			if (user_regs) {
+				groups = reg_user_groups;
+				groupsize = reg_user_size;
+				/* skip over factory reg area */
+				groupno = reg_fact_groups;
+				data_offset += reg_fact_groups * reg_fact_size;
+			} else {
+				groups = reg_fact_groups;
+				groupsize = reg_fact_size;
+				groupno = 0;
+			}
+
+			while (len > 0 && groups > 0) {
+				if (!action) {
+					/*
+					 * Special case: if action is NULL
+					 * we fill buf with otp_info records.
+					 */
+					struct otp_info *otpinfo;
+					map_word lockword;
+					len -= sizeof(struct otp_info);
+					if (len <= 0)
+						return -ENOSPC;
+					ret = do_otp_read(map, chip,
+							  reg_prot_offset,
+							  (u_char *)&lockword,
+							  map_bankwidth(map),
+							  0, 0,  0);
+					if (ret)
+						return ret;
+					otpinfo = (struct otp_info *)buf;
+					otpinfo->start = from;
+					otpinfo->length = groupsize;
+					otpinfo->locked =
+					   !map_word_bitsset(map, lockword,
+							     CMD(1 << groupno));
+					from += groupsize;
+					buf += sizeof(*otpinfo);
+					*retlen += sizeof(*otpinfo);
+				} else if (from >= groupsize) {
+					from -= groupsize;
+					data_offset += groupsize;
+				} else {
+					int size = groupsize;
+					data_offset += from;
+					size -= from;
+					from = 0;
+					if (size > len)
+						size = len;
+					ret = action(map, chip, data_offset,
+						     buf, size, reg_prot_offset,
+						     groupno, groupsize);
+					if (ret < 0)
+						return ret;
+					buf += size;
+					len -= size;
+					*retlen += size;
+					data_offset += size;
+				}
+				groupno++;
+				groups--;
+			}
+
+			/* next OTP region */
+			if (++field == extp->NumProtectionFields)
+				break;
+			reg_prot_offset = otp->ProtRegAddr;
+			reg_fact_groups = otp->FactGroups;
+			reg_fact_size = 1 << otp->FactProtRegSize;
+			reg_user_groups = otp->UserGroups;
+			reg_user_size = 1 << otp->UserProtRegSize;
+			otp++;
+		}
+	}
+
+	return 0;
+}
+
+static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
+					   size_t len, size_t *retlen,
+					    u_char *buf)
+{
+	return cfi_intelext_otp_walk(mtd, from, len, retlen,
+				     buf, do_otp_read, 0);
+}
+
+static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
+					   size_t len, size_t *retlen,
+					    u_char *buf)
+{
+	return cfi_intelext_otp_walk(mtd, from, len, retlen,
+				     buf, do_otp_read, 1);
+}
+
+static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+					    size_t len, size_t *retlen,
+					     u_char *buf)
+{
+	return cfi_intelext_otp_walk(mtd, from, len, retlen,
+				     buf, do_otp_write, 1);
+}
+
+static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
+					   loff_t from, size_t len)
+{
+	size_t retlen;
+	return cfi_intelext_otp_walk(mtd, from, len, &retlen,
+				     NULL, do_otp_lock, 1);
+}
+
+static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, 
+					   struct otp_info *buf, size_t len)
+{
+	size_t retlen;
+	int ret;
+
+	ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0);
+	return ret ? : retlen;
+}
+
+static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
+					   struct otp_info *buf, size_t len)
+{
+	size_t retlen;
+	int ret;
+
+	ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1);
+	return ret ? : retlen;
+}
+
+#endif
+
 static int cfi_intelext_suspend(struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
@@ -1629,9 +2331,18 @@
 				 * as the whole point is that nobody can do anything
 				 * with the chip now anyway.
 				 */
+			} else {
+				/* There seems to be an operation pending. We must wait for it. */
+				printk(KERN_NOTICE "Flash device refused suspend due to pending operation (oldstate %d)\n", chip->oldstate);
+				ret = -EAGAIN;
 			}
 			break;
 		default:
+			/* Should we actually wait? Once upon a time these routines weren't
+			   allowed to. Or should we return -EAGAIN, because the upper layers
+			   ought to have already shut down anything which was using the device
+			   anyway? The latter for now. */
+			printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->oldstate);
 			ret = -EAGAIN;
 		case FL_PM_SUSPENDED:
 			break;
@@ -1652,6 +2363,7 @@
 				   because we're returning failure, and it didn't
 				   get power cycled */
 				chip->state = chip->oldstate;
+				chip->oldstate = FL_READY;
 				wake_up(&chip->wq);
 			}
 			spin_unlock(chip->mutex);
@@ -1676,8 +2388,8 @@
 		
 		/* Go to known state. Chip may have been power cycled */
 		if (chip->state == FL_PM_SUSPENDED) {
-			cfi_write(map, CMD(0xFF), cfi->chips[i].start);
-			chip->state = FL_READY;
+			map_write(map, CMD(0xFF), cfi->chips[i].start);
+			chip->oldstate = chip->state = FL_READY;
 			wake_up(&chip->wq);
 		}
 
@@ -1685,30 +2397,70 @@
 	}
 }
 
+static int cfi_intelext_reset(struct mtd_info *mtd)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	int i, ret;
+
+	for (i=0; i < cfi->numchips; i++) {
+		struct flchip *chip = &cfi->chips[i];
+
+		/* force the completion of any ongoing operation
+		   and switch to array mode so any bootloader in 
+		   flash is accessible for soft reboot. */
+		spin_lock(chip->mutex);
+		ret = get_chip(map, chip, chip->start, FL_SYNCING);
+		if (!ret) {
+			map_write(map, CMD(0xff), chip->start);
+			chip->state = FL_READY;
+		}
+		spin_unlock(chip->mutex);
+	}
+
+	return 0;
+}
+
+static int cfi_intelext_reboot(struct notifier_block *nb, unsigned long val,
+			       void *v)
+{
+	struct mtd_info *mtd;
+
+	mtd = container_of(nb, struct mtd_info, reboot_notifier);
+	cfi_intelext_reset(mtd);
+	return NOTIFY_DONE;
+}
+
 static void cfi_intelext_destroy(struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
+	cfi_intelext_reset(mtd);
+	unregister_reboot_notifier(&mtd->reboot_notifier);
 	kfree(cfi->cmdset_priv);
 	kfree(cfi->cfiq);
+	kfree(cfi->chips[0].priv);
 	kfree(cfi);
 	kfree(mtd->eraseregions);
 }
 
-static char im_name_1[]="cfi_cmdset_0001";
-static char im_name_3[]="cfi_cmdset_0003";
-
-int __init cfi_intelext_init(void)
-{
-	inter_module_register(im_name_1, THIS_MODULE, &cfi_cmdset_0001);
-	inter_module_register(im_name_3, THIS_MODULE, &cfi_cmdset_0001);
+static char im_name_0001[] = "cfi_cmdset_0001";
+static char im_name_0003[] = "cfi_cmdset_0003";
+static char im_name_0200[] = "cfi_cmdset_0200";
+
+static int __init cfi_intelext_init(void)
+{
+	inter_module_register(im_name_0001, THIS_MODULE, &cfi_cmdset_0001);
+	inter_module_register(im_name_0003, THIS_MODULE, &cfi_cmdset_0001);
+	inter_module_register(im_name_0200, THIS_MODULE, &cfi_cmdset_0001);
 	return 0;
 }
 
 static void __exit cfi_intelext_exit(void)
 {
-	inter_module_unregister(im_name_1);
-	inter_module_unregister(im_name_3);
+	inter_module_unregister(im_name_0001);
+	inter_module_unregister(im_name_0003);
+	inter_module_unregister(im_name_0200);
 }
 
 module_init(cfi_intelext_init);
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/cfi_cmdset_0002.c linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0002.c
--- linux-2.4.30-orig/drivers/mtd/chips/cfi_cmdset_0002.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0002.c	2006-12-11 18:18:37.000000000 +0100
@@ -4,16 +4,20 @@
  *
  * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
  * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
+ * Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com>
  *
  * 2_by_8 routines added by Simon Munton
  *
  * 4_by_16 work by Carolyn J. Smith
  *
+ * XIP support hooks by Vitaly Wool (based on code for Intel flash 
+ * by Nicolas Pitre)
+ * 
  * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
  *
  * This code is GPL
  *
- * $Id: cfi_cmdset_0002.c,v 1.98 2004/04/14 11:15:36 dvrabel Exp $
+ * $Id: cfi_cmdset_0002.c,v 1.3 2005/10/12 14:47:50 lel Exp $
  *
  */
 
@@ -34,18 +38,23 @@
 #include <linux/mtd/map.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/cfi.h>
+#include <linux/mtd/xip.h>
 
 #define AMD_BOOTLOC_BUG
 #define FORCE_WORD_WRITE 0
 
+#define MAX_WORD_RETRIES 3
+
+#define MANUFACTURER_AMD	0x0001
+#define MANUFACTURER_SST	0x00BF
+#define SST49LF004B	        0x0060
+#define SST49LF008A		0x005a
 
 static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
 static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
 static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
 static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
-static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t);
-static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t);
 static void cfi_amdstd_sync (struct mtd_info *);
 static int cfi_amdstd_suspend (struct mtd_info *);
 static void cfi_amdstd_resume (struct mtd_info *);
@@ -54,8 +63,11 @@
 static void cfi_amdstd_destroy(struct mtd_info *);
 
 struct mtd_info *cfi_cmdset_0002(struct map_info *, int);
-static struct mtd_info *cfi_amdstd_setup (struct map_info *);
+static struct mtd_info *cfi_amdstd_setup (struct mtd_info *);
 
+static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
+static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
+#include "fwh_lock.h"
 
 static struct mtd_chip_driver cfi_amdstd_chipdrv = {
 	.probe		= NULL, /* Not usable directly */
@@ -65,7 +77,6 @@
 };
 
 
-/* #define DEBUG_LOCK_BITS */
 /* #define DEBUG_CFI_FEATURES */
 
 
@@ -121,8 +132,9 @@
 
 #ifdef AMD_BOOTLOC_BUG
 /* Wheee. Bring me the head of someone at AMD. */
-static void fixup_amd_bootblock(struct map_info *map, void* param)
+static void fixup_amd_bootblock(struct mtd_info *mtd, void* param)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
 	__u8 major = extp->MajorVersion;
@@ -140,25 +152,93 @@
 }
 #endif
 
-static struct cfi_fixup fixup_table[] = {
+static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	if (cfi->cfiq->BufWriteTimeoutTyp) {
+		DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
+		mtd->write = cfi_amdstd_write_buffers;
+	}
+}
+
+static void fixup_use_secsi(struct mtd_info *mtd, void *param)
+{
+	/* Setup for chips with a secsi area */
+	mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
+	mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
+}
+
+static void fixup_use_erase_chip(struct mtd_info *mtd, void *param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	if ((cfi->cfiq->NumEraseRegions == 1) &&
+		((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
+		mtd->erase = cfi_amdstd_erase_chip;
+	}
+	
+}
+
+static struct cfi_fixup cfi_fixup_table[] = {
 #ifdef AMD_BOOTLOC_BUG
-	{
-		0x0001,		/* AMD */
-		CFI_ID_ANY,
-		fixup_amd_bootblock, NULL
-	},
+	{ CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL },
+#endif
+	{ CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x0055, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, },
+#if !FORCE_WORD_WRITE
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
 #endif
 	{ 0, 0, NULL, NULL }
 };
+static struct cfi_fixup jedec_fixup_table[] = {
+	{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
+	{ MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
+	{ 0, 0, NULL, NULL }
+};
+
+static struct cfi_fixup fixup_table[] = {
+	/* The CFI vendor ids and the JEDEC vendor IDs appear
+	 * to be common.  It is like the devices id's are as
+	 * well.  This table is to pick all cases where
+	 * we know that is the case.
+	 */
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL },
+	{ 0, 0, NULL, NULL }
+};
 
 
 struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	unsigned char bootloc;
+	struct mtd_info *mtd;
 	int i;
 
+	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	if (!mtd) {
+		printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
+		return NULL;
+	}
+	memset(mtd, 0, sizeof(*mtd));
+	mtd->priv = map;
+	mtd->type = MTD_NORFLASH;
+
+	/* Fill in the default mtd operations */
+	mtd->erase   = cfi_amdstd_erase_varsize;
+	mtd->write   = cfi_amdstd_write_words;
+	mtd->read    = cfi_amdstd_read;
+	mtd->sync    = cfi_amdstd_sync;
+	mtd->suspend = cfi_amdstd_suspend;
+	mtd->resume  = cfi_amdstd_resume;
+	mtd->flags   = MTD_CAP_NORFLASH;
+	mtd->name    = map->name;
+
 	if (cfi->cfi_mode==CFI_MODE_CFI){
+		unsigned char bootloc;
 		/* 
 		 * It's a real CFI chip, not one for which the probe
 		 * routine faked a CFI structure. So we read the feature
@@ -168,13 +248,26 @@
 		struct cfi_pri_amdstd *extp;
 
 		extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");
-		if (!extp)
+		if (!extp) {
+			kfree(mtd);
 			return NULL;
+		}
+
+		if (extp->MajorVersion != '1' ||
+		    (extp->MinorVersion < '0' || extp->MinorVersion > '4')) {
+			printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
+			       "version %c.%c.\n",  extp->MajorVersion,
+			       extp->MinorVersion);
+			kfree(extp);
+			kfree(mtd);
+			return NULL;
+		}
 
 		/* Install our own private info structure */
 		cfi->cmdset_priv = extp;	
 
-		cfi_fixup(map, fixup_table);
+		/* Apply cfi device specific fixups */
+		cfi_fixup(mtd, cfi_fixup_table);
 
 #ifdef DEBUG_CFI_FEATURES
 		/* Tell the user about it in lots of lovely detail */
@@ -200,38 +293,28 @@
 				cfi->cfiq->EraseRegionInfo[j] = swap;
 			}
 		}
-		/*
-		 * These might already be setup (more correctly) by
-		 * jedec_probe.c - still need it for cfi_probe.c path.
-		 */
-		if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) {
-			switch (cfi->device_type) {
-			case CFI_DEVICETYPE_X8:
-				cfi->addr_unlock1 = 0x555; 
-				cfi->addr_unlock2 = 0x2aa; 
-				break;
-			case CFI_DEVICETYPE_X16:
-				cfi->addr_unlock1 = 0xaaa;
-				if (map->buswidth == cfi->interleave) {
-					/* X16 chip(s) in X8 mode */
-					cfi->addr_unlock2 = 0x555;
-				} else {
-					cfi->addr_unlock2 = 0x554;
-				}
-				break;
-			case CFI_DEVICETYPE_X32:
-				cfi->addr_unlock1 = 0x1555; 
-				cfi->addr_unlock2 = 0xaaa; 
-				break;
-			default:
-				printk(KERN_WARNING
-				       "MTD %s(): Unsupported device type %d\n",
-				       __func__, cfi->device_type);
-				return NULL;
-			}
+		/* Set the default CFI lock/unlock addresses */
+		cfi->addr_unlock1 = 0x555;
+		cfi->addr_unlock2 = 0x2aa;
+		/* Modify the unlock address if we are in compatibility mode */
+		if (	/* x16 in x8 mode */
+			((cfi->device_type == CFI_DEVICETYPE_X8) && 
+				(cfi->cfiq->InterfaceDesc == 2)) ||
+			/* x32 in x16 mode */
+			((cfi->device_type == CFI_DEVICETYPE_X16) &&
+				(cfi->cfiq->InterfaceDesc == 4))) 
+		{
+			cfi->addr_unlock1 = 0xaaa;
+			cfi->addr_unlock2 = 0x555;
 		}
 
 	} /* CFI mode */
+	else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
+		/* Apply jedec specific fixups */
+		cfi_fixup(mtd, jedec_fixup_table);
+	}
+	/* Apply generic fixups */
+	cfi_fixup(mtd, fixup_table);
 
 	for (i=0; i< cfi->numchips; i++) {
 		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
@@ -240,32 +323,22 @@
 	}		
 	
 	map->fldrv = &cfi_amdstd_chipdrv;
-
-	return cfi_amdstd_setup(map);
+	
+	return cfi_amdstd_setup(mtd);
 }
 
 
-static struct mtd_info *cfi_amdstd_setup(struct map_info *map)
+static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct mtd_info *mtd;
 	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
 	unsigned long offset = 0;
 	int i,j;
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
 	printk(KERN_NOTICE "number of %s chips: %d\n", 
 	       (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips);
-
-	if (!mtd) {
-		printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
-		goto setup_err;
-	}
-
-	memset(mtd, 0, sizeof(*mtd));
-	mtd->priv = map;
-	mtd->type = MTD_NORFLASH;
-	/* Also select the correct geometry setup too */ 
+	/* Select the correct geometry setup */ 
 	mtd->size = devsize * cfi->numchips;
 
 	mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
@@ -306,70 +379,10 @@
 	}
 #endif
 
-	switch (CFIDEV_BUSWIDTH)
-	{
-	case 1:
-	case 2:
-	case 4:
-#ifdef CFI_WORD_64
-	case 8:
-#endif
-		if (mtd->numeraseregions == 1
-		    && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) {
-			mtd->erase = cfi_amdstd_erase_chip;
-		} else {
-			mtd->erase = cfi_amdstd_erase_varsize;
-			mtd->lock = cfi_amdstd_lock_varsize;
-			mtd->unlock = cfi_amdstd_unlock_varsize;
-		}
-
-		if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
-			DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
-			mtd->write = cfi_amdstd_write_buffers;
-		} else {
-			DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" );
-			mtd->write = cfi_amdstd_write_words;
-		}
-
-		mtd->read = cfi_amdstd_read;
-		break;
-
-	default:
-		printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
-		       __func__, CFIDEV_BUSWIDTH);
-		goto setup_err;
-		break;
-	}
 	/* FIXME: erase-suspend-program is broken.  See
 	   http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */
 	printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n");
 
-
-	/* does this chip have a secsi area? */
-	if(cfi->mfr==1){
-		
-		switch(cfi->id){
-		case 0x50:
-		case 0x53:
-		case 0x55:
-		case 0x56:
-		case 0x5C:
-		case 0x5F:
-			/* Yes */
-			mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
-			mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
-		default:		       
-			;
-		}
-	}
-	
-		
-	mtd->sync = cfi_amdstd_sync;
-	mtd->suspend = cfi_amdstd_suspend;
-	mtd->resume = cfi_amdstd_resume;
-	mtd->flags = MTD_CAP_NORFLASH;
-	map->fldrv = &cfi_amdstd_chipdrv;
-	mtd->name = map->name;
 	__module_get(THIS_MODULE);
 	return mtd;
 
@@ -395,14 +408,40 @@
  * correctly and is therefore not done	(particulary with interleaved chips
  * as each chip must be checked independantly of the others).
  */
-static int chip_ready(struct map_info *map, unsigned long addr)
+static int __xipram chip_ready(struct map_info *map, unsigned long addr)
+{
+	map_word d, t;
+
+	d = map_read(map, addr);
+	t = map_read(map, addr);
+
+	return map_word_equal(map, d, t);
+}
+
+/*
+ * Return true if the chip is ready and has the correct value.
+ *
+ * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
+ * non-suspended sector) and it is indicated by no bits toggling.
+ *
+ * Error are indicated by toggling bits or bits held with the wrong value,
+ * or with bits toggling.
+ *
+ * Note that anything more complicated than checking if no bits are toggling
+ * (including checking DQ5 for an error status) is tricky to get working
+ * correctly and is therefore not done	(particulary with interleaved chips
+ * as each chip must be checked independantly of the others).
+ *
+ */
+static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word expected)
 {
-    cfi_word d, t;
+	map_word oldd, curd;
 
-    d = cfi_read(map, addr);
-    t = d ^ cfi_read(map, addr);
+	oldd = map_read(map, addr);
+	curd = map_read(map, addr);
 
-    return t == 0;
+	return	map_word_equal(map, oldd, curd) && 
+		map_word_equal(map, curd, expected);
 }
 
 static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
@@ -424,12 +463,12 @@
 
 			if (time_after(jiffies, timeo)) {
 				printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
-				cfi_spin_unlock(chip->mutex);
+				spin_unlock(chip->mutex);
 				return -EIO;
 			}
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 			cfi_udelay(1);
-			cfi_spin_lock(chip->mutex);
+			spin_lock(chip->mutex);
 			/* Someone else might have been playing with it. */
 			goto retry;
 		}
@@ -444,6 +483,7 @@
 			goto sleep;
 
 		if (!(mode == FL_READY || mode == FL_POINT
+		      || !cfip
 		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
 		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1))))
 			goto sleep;
@@ -455,7 +495,7 @@
 		/* Erase suspend */
 		/* It's harmless to issue the Erase-Suspend and Erase-Resume
 		 * commands when the erase algorithm isn't in progress. */
-		cfi_write(map, CMD(0xB0), chip->in_progress_block_addr);
+		map_write(map, CMD(0xB0), chip->in_progress_block_addr);
 		chip->oldstate = FL_ERASING;
 		chip->state = FL_ERASE_SUSPENDING;
 		chip->erase_suspended = 1;
@@ -469,22 +509,30 @@
 				 * there was an error (so leave the erase
 				 * routine to recover from it) or we trying to
 				 * use the erase-in-progress sector. */
-				cfi_write(map, CMD(0x30), chip->in_progress_block_addr);
+				map_write(map, CMD(0x30), chip->in_progress_block_addr);
 				chip->state = FL_ERASING;
 				chip->oldstate = FL_READY;
 				printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
 				return -EIO;
 			}
 			
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 			cfi_udelay(1);
-			cfi_spin_lock(chip->mutex);
+			spin_lock(chip->mutex);
 			/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
 			   So we can just loop here. */
 		}
 		chip->state = FL_READY;
 		return 0;
 
+	case FL_XIP_WHILE_ERASING:
+		if (mode != FL_READY && mode != FL_POINT &&
+		    (!cfip || !(cfip->EraseSuspend&2)))
+			goto sleep;
+		chip->oldstate = chip->state;
+		chip->state = FL_READY;
+		return 0;
+
 	case FL_POINT:
 		/* Only if there's no operation suspended... */
 		if (mode == FL_READY && chip->oldstate == FL_READY)
@@ -494,10 +542,10 @@
 	sleep:
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		add_wait_queue(&chip->wq, &wait);
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 		schedule();
 		remove_wait_queue(&chip->wq, &wait);
-		cfi_spin_lock(chip->mutex);
+		spin_lock(chip->mutex);
 		goto resettime;
 	}
 }
@@ -510,11 +558,16 @@
 	switch(chip->oldstate) {
 	case FL_ERASING:
 		chip->state = chip->oldstate;
-		cfi_write(map, CMD(0x30), chip->in_progress_block_addr);
+		map_write(map, CMD(0x30), chip->in_progress_block_addr);
 		chip->oldstate = FL_READY;
 		chip->state = FL_ERASING;
 		break;
 
+	case FL_XIP_WHILE_ERASING:
+		chip->state = chip->oldstate;
+		chip->oldstate = FL_READY;
+		break;
+
 	case FL_READY:
 	case FL_STATUS:
 		/* We should really make set_vpp() count, rather than doing this */
@@ -526,6 +579,198 @@
 	wake_up(&chip->wq);
 }
 
+#ifdef CONFIG_MTD_XIP
+
+/*
+ * No interrupt what so ever can be serviced while the flash isn't in array
+ * mode.  This is ensured by the xip_disable() and xip_enable() functions
+ * enclosing any code path where the flash is known not to be in array mode.
+ * And within a XIP disabled code path, only functions marked with __xipram
+ * may be called and nothing else (it's a good thing to inspect generated
+ * assembly to make sure inline functions were actually inlined and that gcc
+ * didn't emit calls to its own support functions). Also configuring MTD CFI
+ * support to a single buswidth and a single interleave is also recommended.
+ */
+
+static void xip_disable(struct map_info *map, struct flchip *chip,
+			unsigned long adr)
+{
+	/* TODO: chips with no XIP use should ignore and return */
+	(void) map_read(map, adr); /* ensure mmu mapping is up to date */
+	local_irq_disable();
+}
+
+static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
+				unsigned long adr)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+
+	if (chip->state != FL_POINT && chip->state != FL_READY) {
+		map_write(map, CMD(0xf0), adr);
+		chip->state = FL_READY;
+	}
+	(void) map_read(map, adr);
+	xip_iprefetch();
+	local_irq_enable();
+}
+
+/*
+ * When a delay is required for the flash operation to complete, the
+ * xip_udelay() function is polling for both the given timeout and pending
+ * (but still masked) hardware interrupts.  Whenever there is an interrupt
+ * pending then the flash erase operation is suspended, array mode restored 
+ * and interrupts unmasked.  Task scheduling might also happen at that
+ * point.  The CPU eventually returns from the interrupt or the call to
+ * schedule() and the suspended flash operation is resumed for the remaining
+ * of the delay period.
+ *
+ * Warning: this function _will_ fool interrupt latency tracing tools.
+ */
+
+static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
+				unsigned long adr, int usec)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+	map_word status, OK = CMD(0x80);
+	unsigned long suspended, start = xip_currtime();
+	flstate_t oldstate;
+
+	do {
+		cpu_relax();
+		if (xip_irqpending() && extp &&
+		    ((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) &&
+		    (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
+			/*
+			 * Let's suspend the erase operation when supported.  
+			 * Note that we currently don't try to suspend 
+			 * interleaved chips if there is already another 
+			 * operation suspended (imagine what happens
+			 * when one chip was already done with the current
+			 * operation while another chip suspended it, then
+			 * we resume the whole thing at once).  Yes, it
+			 * can happen!
+			 */
+			map_write(map, CMD(0xb0), adr);
+			usec -= xip_elapsed_since(start);
+			suspended = xip_currtime();
+			do {
+				if (xip_elapsed_since(suspended) > 100000) {
+					/*
+					 * The chip doesn't want to suspend
+					 * after waiting for 100 msecs.
+					 * This is a critical error but there
+					 * is not much we can do here.
+					 */
+					return;
+				}
+				status = map_read(map, adr);
+			} while (!map_word_andequal(map, status, OK, OK));
+
+			/* Suspend succeeded */
+			oldstate = chip->state;
+			if (!map_word_bitsset(map, status, CMD(0x40)))
+				break;
+			chip->state = FL_XIP_WHILE_ERASING;
+			chip->erase_suspended = 1;
+			map_write(map, CMD(0xf0), adr);
+			(void) map_read(map, adr);
+			asm volatile (".rep 8; nop; .endr");
+			local_irq_enable();
+			spin_unlock(chip->mutex);
+			asm volatile (".rep 8; nop; .endr");
+			cond_resched();
+
+			/*
+			 * We're back.  However someone else might have
+			 * decided to go write to the chip if we are in
+			 * a suspended erase state.  If so let's wait
+			 * until it's done.
+			 */
+			spin_lock(chip->mutex);
+			while (chip->state != FL_XIP_WHILE_ERASING) {
+				DECLARE_WAITQUEUE(wait, current);
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				add_wait_queue(&chip->wq, &wait);
+				spin_unlock(chip->mutex);
+				schedule();
+				remove_wait_queue(&chip->wq, &wait);
+				spin_lock(chip->mutex);
+			}
+			/* Disallow XIP again */
+			local_irq_disable();
+
+			/* Resume the write or erase operation */
+			map_write(map, CMD(0x30), adr);
+			chip->state = oldstate;
+			start = xip_currtime();
+		} else if (usec >= 1000000/HZ) {
+			/*
+			 * Try to save on CPU power when waiting delay
+			 * is at least a system timer tick period.
+			 * No need to be extremely accurate here.
+			 */
+			xip_cpu_idle();
+		}
+		status = map_read(map, adr);
+	} while (!map_word_andequal(map, status, OK, OK)
+		 && xip_elapsed_since(start) < usec);
+}
+
+#define UDELAY(map, chip, adr, usec)  xip_udelay(map, chip, adr, usec)
+
+/*
+ * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
+ * the flash is actively programming or erasing since we have to poll for
+ * the operation to complete anyway.  We can't do that in a generic way with
+ * a XIP setup so do it before the actual flash operation in this case
+ * and stub it out from INVALIDATE_CACHE_UDELAY.
+ */
+#define XIP_INVAL_CACHED_RANGE(map, from, size)  \
+	INVALIDATE_CACHED_RANGE(map, from, size)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
+	UDELAY(map, chip, adr, usec)
+
+/*
+ * Extra notes:
+ *
+ * Activating this XIP support changes the way the code works a bit.  For
+ * example the code to suspend the current process when concurrent access
+ * happens is never executed because xip_udelay() will always return with the
+ * same chip state as it was entered with.  This is why there is no care for
+ * the presence of add_wait_queue() or schedule() calls from within a couple
+ * xip_disable()'d  areas of code, like in do_erase_oneblock for example.
+ * The queueing and scheduling are always happening within xip_udelay().
+ *
+ * Similarly, get_chip() and put_chip() just happen to always be executed
+ * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
+ * is in array mode, therefore never executing many cases therein and not
+ * causing any problem with XIP.
+ */
+
+#else
+
+#define xip_disable(map, chip, adr)
+#define xip_enable(map, chip, adr)
+#define XIP_INVAL_CACHED_RANGE(x...)
+
+#define UDELAY(map, chip, adr, usec)  \
+do {  \
+	spin_unlock(chip->mutex);  \
+	cfi_udelay(usec);  \
+	spin_lock(chip->mutex);  \
+} while (0)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
+do {  \
+	spin_unlock(chip->mutex);  \
+	INVALIDATE_CACHED_RANGE(map, adr, len);  \
+	cfi_udelay(usec);  \
+	spin_lock(chip->mutex);  \
+} while (0)
+
+#endif
 
 static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
 {
@@ -536,17 +781,17 @@
 	adr += chip->start;
 
 	/* Ensure cmd read/writes are aligned. */ 
-	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
+	cmd_addr = adr & ~(map_bankwidth(map)-1); 
 
-	cfi_spin_lock(chip->mutex);
+	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, cmd_addr, FL_READY);
 	if (ret) {
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 		return ret;
 	}
 
 	if (chip->state != FL_POINT && chip->state != FL_READY) {
-		cfi_write(map, CMD(0xf0), cmd_addr);
+		map_write(map, CMD(0xf0), cmd_addr);
 		chip->state = FL_READY;
 	}
 
@@ -554,7 +799,7 @@
 
 	put_chip(map, chip, cmd_addr);
 
-	cfi_spin_unlock(chip->mutex);
+	spin_unlock(chip->mutex);
 	return 0;
 }
 
@@ -608,7 +853,7 @@
 	struct cfi_private *cfi = map->fldrv_priv;
 
  retry:
-	cfi_spin_lock(chip->mutex);
+	spin_lock(chip->mutex);
 
 	if (chip->state != FL_READY){
 #if 0
@@ -617,7 +862,7 @@
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		add_wait_queue(&chip->wq, &wait);
 		
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 
 		schedule();
 		remove_wait_queue(&chip->wq, &wait);
@@ -634,21 +879,19 @@
 
 	chip->state = FL_READY;
 
-	/* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
 	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
 	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
 	cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
 	
 	map_copy_from(map, buf, adr, len);
 
-	/* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
 	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
 	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
 	cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
 	cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
 	
 	wake_up(&chip->wq);
-	cfi_spin_unlock(chip->mutex);
+	spin_unlock(chip->mutex);
 
 	return 0;
 }
@@ -697,7 +940,7 @@
 }
 
 
-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
+static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	unsigned long timeo = jiffies + HZ;
@@ -712,18 +955,20 @@
 	 */
 	unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
 	int ret = 0;
+	map_word oldd;
+	int retry_cnt = 0;
 
 	adr += chip->start;
 
-	cfi_spin_lock(chip->mutex);
+	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, adr, FL_WRITING);
 	if (ret) {
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 		return ret;
 	}
 
-	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
-	       __func__, adr, datum );
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
+	       __func__, adr, datum.x[0] );
 
 	/*
 	 * Check for a NOP for the case when the datum to write is already
@@ -731,30 +976,26 @@
 	 * data at other locations when 0xff is written to a location that
 	 * already contains 0xff.
 	 */
-	if (cfi_read(map, adr) == datum) {
+	oldd = map_read(map, adr);
+	if (map_word_equal(map, oldd, datum)) {
 		DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP\n",
 		       __func__);
 		goto op_done;
 	}
 
+	XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
 	ENABLE_VPP(map);
-	/*
-	 * The CFI_DEVICETYPE_X8 argument is needed even when
-	 * cfi->device_type != CFI_DEVICETYPE_X8.  The addresses for
-	 * command sequences don't scale even when the device is
-	 * wider.  This is the case for many of the cfi_send_gen_cmd()
-	 * below.  I'm not sure, however, why some use
-	 * cfi->device_type.
-	 */
-	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_write(map, datum, adr);
+	xip_disable(map, chip, adr);
+ retry:
+	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	map_write(map, datum, adr);
 	chip->state = FL_WRITING;
 
-	cfi_spin_unlock(chip->mutex);
-	cfi_udelay(chip->word_write_time);
-	cfi_spin_lock(chip->mutex);
+	INVALIDATE_CACHE_UDELAY(map, chip,
+				adr, map_bankwidth(map),
+				chip->word_write_time);
 
 	/* See comment above for timeout value. */
 	timeo = jiffies + uWriteTimeout; 
@@ -765,39 +1006,43 @@
 
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&chip->wq, &wait);
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 			schedule();
 			remove_wait_queue(&chip->wq, &wait);
 			timeo = jiffies + (HZ / 2); /* FIXME */
-			cfi_spin_lock(chip->mutex);
+			spin_lock(chip->mutex);
 			continue;
 		}
 
 		if (chip_ready(map, adr))
-			goto op_done;
-
-		if (time_after(jiffies, timeo))
 			break;
 
+		if (time_after(jiffies, timeo)) {
+			xip_enable(map, chip, adr);
+			printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
+			xip_disable(map, chip, adr);
+                        break;
+		}
+
 		/* Latency issues. Drop the lock, wait a while and retry */
-		cfi_spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		cfi_spin_lock(chip->mutex);
+		UDELAY(map, chip, adr, 1);
 	}
+	/* Did we succeed? */
+	if (!chip_good(map, adr, datum)) {
+		/* reset on all failures. */
+		map_write( map, CMD(0xF0), chip->start );
+		/* FIXME - should have reset delay before continuing */
 
-	printk(KERN_WARNING "MTD %s(): software timeout\n",
-	       __func__ );
-
- op_failed:
-	/* reset on all failures. */
-	cfi_write( map, CMD(0xF0), chip->start );
-	/* FIXME - should have reset delay before continuing */
-	ret = -EIO;
+		if (++retry_cnt <= MAX_WORD_RETRIES) 
+			goto retry;
 
+		ret = -EIO;
+	}
+	xip_enable(map, chip, adr);
  op_done:
 	chip->state = FL_READY;
 	put_chip(map, chip, adr);
-	cfi_spin_unlock(chip->mutex);
+	spin_unlock(chip->mutex);
 
 	return ret;
 }
@@ -822,15 +1067,14 @@
 	chipstart = cfi->chips[chipnum].start;
 
 	/* If it's not bus-aligned, do the first byte write */
-	if (ofs & (CFIDEV_BUSWIDTH-1)) {
-		unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
+	if (ofs & (map_bankwidth(map)-1)) {
+		unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1);
 		int i = ofs - bus_ofs;
 		int n = 0;
-		u_char tmp_buf[8];
-		cfi_word datum;
+		map_word tmp_buf;
 
  retry:
-		cfi_spin_lock(cfi->chips[chipnum].mutex);
+		spin_lock(cfi->chips[chipnum].mutex);
 
 		if (cfi->chips[chipnum].state != FL_READY) {
 #if 0
@@ -839,7 +1083,7 @@
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
 
-			cfi_spin_unlock(cfi->chips[chipnum].mutex);
+			spin_unlock(cfi->chips[chipnum].mutex);
 
 			schedule();
 			remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
@@ -850,38 +1094,25 @@
 			goto retry;
 		}
 
-		map_copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+		/* Load 'tmp_buf' with old contents of flash */
+		tmp_buf = map_read(map, bus_ofs+chipstart);
 
-		cfi_spin_unlock(cfi->chips[chipnum].mutex);
+		spin_unlock(cfi->chips[chipnum].mutex);
 
-		while (len && i < CFIDEV_BUSWIDTH) {
-			tmp_buf[i++] = buf[n++];
-			len--;
-		}
-
-		/* already know that buswidth > 1 */
-		if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)tmp_buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)tmp_buf;
-#ifdef CFI_WORD_64
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)tmp_buf;
-#endif
-		} else {
-			printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
-			       __func__, CFIDEV_BUSWIDTH);
-			return -EINVAL;
-		}
+		/* Number of bytes to copy from buffer */
+		n = min_t(int, len, map_bankwidth(map)-i);
+		
+		tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum], 
-				       bus_ofs, datum);
+				       bus_ofs, tmp_buf);
 		if (ret) 
 			return ret;
 		
 		ofs += n;
 		buf += n;
 		(*retlen) += n;
+		len -= n;
 
 		if (ofs >> cfi->chipshift) {
 			chipnum ++; 
@@ -892,33 +1123,20 @@
 	}
 	
 	/* We are now aligned, write as much as possible */
-	while(len >= CFIDEV_BUSWIDTH) {
-		cfi_word datum;
+	while(len >= map_bankwidth(map)) {
+		map_word datum;
+
+		datum = map_word_load(map, buf);
 
-		if (cfi_buswidth_is_1()) {
-			datum = *(__u8*)buf;
-		} else if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)buf;
-#ifdef CFI_WORD_64
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)buf;
-#endif
-		} else {
-			printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
-			       __func__, CFIDEV_BUSWIDTH);
-			return -EINVAL;
-		}
 		ret = do_write_oneword(map, &cfi->chips[chipnum],
 				       ofs, datum);
 		if (ret)
 			return ret;
 
-		ofs += CFIDEV_BUSWIDTH;
-		buf += CFIDEV_BUSWIDTH;
-		(*retlen) += CFIDEV_BUSWIDTH;
-		len -= CFIDEV_BUSWIDTH;
+		ofs += map_bankwidth(map);
+		buf += map_bankwidth(map);
+		(*retlen) += map_bankwidth(map);
+		len -= map_bankwidth(map);
 
 		if (ofs >> cfi->chipshift) {
 			chipnum ++; 
@@ -930,13 +1148,11 @@
 	}
 
 	/* Write the trailing bytes if any */
-	if (len & (CFIDEV_BUSWIDTH-1)) {
-		int i = 0, n = 0;
-		u_char tmp_buf[8];
-		cfi_word datum;
+	if (len & (map_bankwidth(map)-1)) {
+		map_word tmp_buf;
 
  retry1:
-		cfi_spin_lock(cfi->chips[chipnum].mutex);
+		spin_lock(cfi->chips[chipnum].mutex);
 
 		if (cfi->chips[chipnum].state != FL_READY) {
 #if 0
@@ -945,7 +1161,7 @@
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
 
-			cfi_spin_unlock(cfi->chips[chipnum].mutex);
+			spin_unlock(cfi->chips[chipnum].mutex);
 
 			schedule();
 			remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
@@ -956,33 +1172,18 @@
 			goto retry1;
 		}
 
-		map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+		tmp_buf = map_read(map, ofs + chipstart);
 
-		cfi_spin_unlock(cfi->chips[chipnum].mutex);
-
-		while (len--)
-			tmp_buf[i++] = buf[n++];
-
-		if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)tmp_buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)tmp_buf;
-#ifdef CFI_WORD_64
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)tmp_buf;
-#endif
-		} else {
-			printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
-			       __func__, CFIDEV_BUSWIDTH);
-			return -EINVAL;
-		}
+		spin_unlock(cfi->chips[chipnum].mutex);
 
+		tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
+	
 		ret = do_write_oneword(map, &cfi->chips[chipnum], 
-				ofs, datum);
+				ofs, tmp_buf);
 		if (ret) 
 			return ret;
 		
-		(*retlen) += n;
+		(*retlen) += len;
 	}
 
 	return 0;
@@ -992,124 +1193,70 @@
 /*
  * FIXME: interleaved mode not tested, and probably not supported!
  */
-static inline int do_write_buffer(struct map_info *map, struct flchip *chip, 
-				  unsigned long adr, const u_char *buf, int len)
+static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
+				    unsigned long adr, const u_char *buf, 
+				    int len)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	unsigned long timeo = jiffies + HZ;
 	/* see comments in do_write_oneword() regarding uWriteTimeo. */
-	static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+	unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
 	int ret = -EIO;
 	unsigned long cmd_adr;
-	int z, bytes, words;
-	cfi_word datum;
+	int z, words;
+	map_word datum;
 
 	adr += chip->start;
 	cmd_adr = adr;
 
-	cfi_spin_lock(chip->mutex);
+	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, adr, FL_WRITING);
 	if (ret) {
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 		return ret;
 	}
 
-	if (cfi_buswidth_is_1()) {
-		datum = *(__u8*)buf;
-	} else if (cfi_buswidth_is_2()) {
-		datum = *(__u16*)buf;
-	} else if (cfi_buswidth_is_4()) {
-		datum = *(__u32*)buf;
-#ifdef CFI_WORD_64
-	} else if (cfi_buswidth_is_8()) {
-		datum = *(__u64*)buf;
-#endif
-	} else {
-		printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
-		       __func__, CFIDEV_BUSWIDTH);
-		return -EINVAL;
-	}
+	datum = map_word_load(map, buf);
 
-	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
-	       __func__, adr, datum );
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
+	       __func__, adr, datum.x[0] );
 
+	XIP_INVAL_CACHED_RANGE(map, adr, len);
 	ENABLE_VPP(map);
-	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+	xip_disable(map, chip, cmd_adr);
+	
+	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+	//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
 
 	/* Write Buffer Load */
-	cfi_write(map, CMD(0x25), cmd_adr);
+	map_write(map, CMD(0x25), cmd_adr);
 
 	chip->state = FL_WRITING_TO_BUFFER;
 
 	/* Write length of data to come */
-	bytes = len & (CFIDEV_BUSWIDTH-1);
-	words = len / CFIDEV_BUSWIDTH;
-	cfi_write(map, CMD(words - !bytes), cmd_adr );
+	words = len / map_bankwidth(map);
+	map_write(map, CMD(words - 1), cmd_adr);
 	/* Write data */
 	z = 0;
-	while(z < words * CFIDEV_BUSWIDTH) {
-		if (cfi_buswidth_is_1()) {
-			datum = *((__u8*)buf);
-			map_write8 (map, *((__u8*)buf)++, adr+z);
-		} else if (cfi_buswidth_is_2()) {
-			datum = *((__u16*)buf);
-			map_write16 (map, *((__u16*)buf)++, adr+z);
-		} else if (cfi_buswidth_is_4()) {
-			datum = *((__u32*)buf);
-			map_write32 (map, *((__u32*)buf)++, adr+z);
-#ifdef CFI_WORD_64
-		} else if (cfi_buswidth_is_8()) {
-			datum = *((__u64*)buf);
-			map_write64 (map, *((__u64*)buf)++, adr+z);
-#endif
-		} else {
-			printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
-			       __func__, CFIDEV_BUSWIDTH);
-			ret = -EINVAL;
-			goto op_failed;
-		}
-		z += CFIDEV_BUSWIDTH;
-	}
-	if (bytes) {
-		int i = 0, n = 0;
-		u_char tmp_buf[8], *tmp_p = tmp_buf;
-
-		while (bytes--)
-			tmp_buf[i++] = buf[n++];
-		while (i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = 0xff;
-		if (cfi_buswidth_is_2()) {
-			datum = *((__u16*)tmp_p);
-			map_write16 (map, *((__u16*)tmp_p)++, adr+z);
-		} else if (cfi_buswidth_is_4()) {
-			datum = *((__u32*)tmp_p);
-			map_write32 (map, *((__u32*)tmp_p)++, adr+z);
-#ifdef CFI_WORD_64
-		} else if (cfi_buswidth_is_8()) {
-			datum = *((__u64*)tmp_p);
-			map_write64 (map, *((__u64*)tmp_p)++, adr+z);
-#endif
-		} else {
-			printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
-			       __func__, CFIDEV_BUSWIDTH);
-			ret = -EINVAL;
-			goto op_failed;
-		}
-	} else if (words > 0) {
-		z -= CFIDEV_BUSWIDTH;
+	while(z < words * map_bankwidth(map)) {
+		datum = map_word_load(map, buf);
+		map_write(map, datum, adr + z);
+
+		z += map_bankwidth(map);
+		buf += map_bankwidth(map);
 	}
+	z -= map_bankwidth(map);
 
 	adr += z;
 
 	/* Write Buffer Program Confirm: GO GO GO */
-	cfi_write(map, CMD(0x29), cmd_adr);
+	map_write(map, CMD(0x29), cmd_adr);
 	chip->state = FL_WRITING;
 
-	cfi_spin_unlock(chip->mutex);
-	cfi_udelay(chip->buffer_write_time);
-	cfi_spin_lock(chip->mutex);
+	INVALIDATE_CACHE_UDELAY(map, chip,
+				adr, map_bankwidth(map),
+				chip->word_write_time);
 
 	timeo = jiffies + uWriteTimeout; 
 		
@@ -1120,39 +1267,39 @@
 
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&chip->wq, &wait);
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 			schedule();
 			remove_wait_queue(&chip->wq, &wait);
 			timeo = jiffies + (HZ / 2); /* FIXME */
-			cfi_spin_lock(chip->mutex);
+			spin_lock(chip->mutex);
 			continue;
 		}
 
-		if (chip_ready(map, adr))
+		if (chip_ready(map, adr)) {
+			xip_enable(map, chip, adr);
 			goto op_done;
+		}
 		    
 		if( time_after(jiffies, timeo))
 			break;
 
 		/* Latency issues. Drop the lock, wait a while and retry */
-		cfi_spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		cfi_spin_lock(chip->mutex);
+		UDELAY(map, chip, adr, 1);
 	}
 
+	/* reset on all failures. */
+	map_write( map, CMD(0xF0), chip->start );
+	xip_enable(map, chip, adr);
+	/* FIXME - should have reset delay before continuing */
+
 	printk(KERN_WARNING "MTD %s(): software timeout\n",
 	       __func__ );
 
- op_failed:
-	/* reset on all failures. */
-	cfi_write( map, CMD(0xF0), chip->start );
-	/* FIXME - should have reset delay before continuing */
 	ret = -EIO;
-
  op_done:
 	chip->state = FL_READY;
 	put_chip(map, chip, adr);
-	cfi_spin_unlock(chip->mutex);
+	spin_unlock(chip->mutex);
 
 	return ret;
 }
@@ -1163,7 +1310,7 @@
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
 	int ret = 0;
 	int chipnum;
 	unsigned long ofs;
@@ -1176,12 +1323,12 @@
 	ofs = to  - (chipnum << cfi->chipshift);
 
 	/* If it's not bus-aligned, do the first word write */
-	if (ofs & (CFIDEV_BUSWIDTH-1)) {
-		size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1);
+	if (ofs & (map_bankwidth(map)-1)) {
+		size_t local_len = (-ofs)&(map_bankwidth(map)-1);
 		if (local_len > len)
 			local_len = len;
-		ret = cfi_amdstd_write_words(mtd, to, local_len,
-					       retlen, buf);
+		ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
+					     local_len, retlen, buf);
 		if (ret)
 			return ret;
 		ofs += local_len;
@@ -1197,12 +1344,15 @@
 	}
 
 	/* Write buffer is worth it only if more than one word to write... */
-	while (len) {
+	while (len >= map_bankwidth(map) * 2) {
 		/* We must not cross write block boundaries */
 		int size = wbufsize - (ofs & (wbufsize-1));
 
 		if (size > len)
 			size = len;
+		if (size % map_bankwidth(map))
+			size -= size % map_bankwidth(map);
+
 		ret = do_write_buffer(map, &cfi->chips[chipnum], 
 				      ofs, buf, size);
 		if (ret)
@@ -1221,6 +1371,16 @@
 		}
 	}
 
+	if (len) {
+		size_t retlen_dregs = 0;
+
+		ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
+					     len, &retlen_dregs, buf);
+
+		*retlen += retlen_dregs;
+		return ret;
+	}
+
 	return 0;
 }
 
@@ -1229,7 +1389,7 @@
  * Handle devices with one erase region, that only implement
  * the chip erase command.
  */
-static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
+static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	unsigned long timeo = jiffies + HZ;
@@ -1239,32 +1399,34 @@
 
 	adr = cfi->addr_unlock1;
 
-	cfi_spin_lock(chip->mutex);
+	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, adr, FL_WRITING);
 	if (ret) {
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 		return ret;
 	}
 
 	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
 	       __func__, chip->start );
 
+	XIP_INVAL_CACHED_RANGE(map, adr, map->size);
 	ENABLE_VPP(map);
-	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+	xip_disable(map, chip, adr);
+
+	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
 
 	chip->state = FL_ERASING;
 	chip->erase_suspended = 0;
 	chip->in_progress_block_addr = adr;
 
-	cfi_spin_unlock(chip->mutex);
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout((chip->erase_time*HZ)/(2*1000));
-	cfi_spin_lock(chip->mutex);
+	INVALIDATE_CACHE_UDELAY(map, chip,
+				adr, map->size,
+				chip->erase_time*500);
 
 	timeo = jiffies + (HZ*20);
 
@@ -1273,10 +1435,10 @@
 			/* Someone's suspended the erase. Sleep */
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&chip->wq, &wait);
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 			schedule();
 			remove_wait_queue(&chip->wq, &wait);
-			cfi_spin_lock(chip->mutex);
+			spin_lock(chip->mutex);
 			continue;
 		}
 		if (chip->erase_suspended) {
@@ -1287,130 +1449,36 @@
 		}
 
 		if (chip_ready(map, adr))
-			goto op_done;
+			break;
 
-		if (time_after(jiffies, timeo))
+		if (time_after(jiffies, timeo)) {
+			printk(KERN_WARNING "MTD %s(): software timeout\n",
+				__func__ );
 			break;
+		}
 
 		/* Latency issues. Drop the lock, wait a while and retry */
-		cfi_spin_unlock(chip->mutex);
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-		cfi_spin_lock(chip->mutex);
+		UDELAY(map, chip, adr, 1000000/HZ);
 	}
+	/* Did we succeed? */
+	if (!chip_good(map, adr, map_word_ff(map))) {
+		/* reset on all failures. */
+		map_write( map, CMD(0xF0), chip->start );
+		/* FIXME - should have reset delay before continuing */
 
-	printk(KERN_WARNING "MTD %s(): software timeout\n",
-	       __func__ );
-
- op_failed:
-	/* reset on all failures. */
-	cfi_write( map, CMD(0xF0), chip->start );
-	/* FIXME - should have reset delay before continuing */
-	ret = -EIO;
+		ret = -EIO;
+	}
 
- op_done:
 	chip->state = FL_READY;
+	xip_enable(map, chip, adr);
 	put_chip(map, chip, adr);
-	cfi_spin_unlock(chip->mutex);
+	spin_unlock(chip->mutex);
 
 	return ret;
 }
 
 
-typedef int (*frob_t)(struct map_info *map, struct flchip *chip,
-		      unsigned long adr, void *thunk);
-
-
-static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob,
-				   loff_t ofs, size_t len, void *thunk)
-{
-	struct map_info *map = mtd->priv;
-	struct cfi_private *cfi = map->fldrv_priv;
-	unsigned long adr;
-	int chipnum, ret = 0;
-	int i, first;
-	struct mtd_erase_region_info *regions = mtd->eraseregions;
-
-	if (ofs > mtd->size)
-		return -EINVAL;
-
-	if ((len + ofs) > 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 && ofs >= 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 (ofs & (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 && (ofs + len) >= regions[i].offset)
-		i++;
-
-	/* As before, drop back one to point at the region in which
-	   the address actually falls
-	*/
-	i--;
-	
-	if ((ofs + len) & (regions[i].erasesize-1))
-		return -EINVAL;
-
-	chipnum = ofs >> cfi->chipshift;
-	adr = ofs - (chipnum << cfi->chipshift);
-
-	i=first;
-
-	while (len) {
-		ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
-		
-		if (ret)
-			return ret;
-
-		adr += regions[i].erasesize;
-		len -= regions[i].erasesize;
-
-		if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
-			i++;
-
-		if (adr >> cfi->chipshift) {
-			adr = 0;
-			chipnum++;
-			
-			if (chipnum >= cfi->numchips)
-			break;
-		}
-	}
-
-	return 0;
-}
-
-
-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	unsigned long timeo = jiffies + HZ;
@@ -1419,32 +1487,34 @@
 
 	adr += chip->start;
 
-	cfi_spin_lock(chip->mutex);
+	spin_lock(chip->mutex);
 	ret = get_chip(map, chip, adr, FL_ERASING);
 	if (ret) {
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 		return ret;
 	}
 
 	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
 	       __func__, adr );
 
+	XIP_INVAL_CACHED_RANGE(map, adr, len);
 	ENABLE_VPP(map);
-	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	cfi_write(map, CMD(0x30), adr);
+	xip_disable(map, chip, adr);
+
+	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+	map_write(map, CMD(0x30), adr);
 
 	chip->state = FL_ERASING;
 	chip->erase_suspended = 0;
 	chip->in_progress_block_addr = adr;
-	
-	cfi_spin_unlock(chip->mutex);
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout((chip->erase_time*HZ)/(2*1000));
-	cfi_spin_lock(chip->mutex);
+
+	INVALIDATE_CACHE_UDELAY(map, chip,
+				adr, len,
+				chip->erase_time*500);
 
 	timeo = jiffies + (HZ*20);
 
@@ -1453,10 +1523,10 @@
 			/* Someone's suspended the erase. Sleep */
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&chip->wq, &wait);
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 			schedule();
 			remove_wait_queue(&chip->wq, &wait);
-			cfi_spin_lock(chip->mutex);
+			spin_lock(chip->mutex);
 			continue;
 		}
 		if (chip->erase_suspended) {
@@ -1466,32 +1536,33 @@
 			chip->erase_suspended = 0;
 		}
 
-		if (chip_ready(map, adr))
-			goto op_done;
+		if (chip_ready(map, adr)) {
+			xip_enable(map, chip, adr);
+			break;
+		}
 
-		if (time_after(jiffies, timeo))
+		if (time_after(jiffies, timeo)) {
+			xip_enable(map, chip, adr);
+			printk(KERN_WARNING "MTD %s(): software timeout\n",
+				__func__ );
 			break;
+		}
 
 		/* Latency issues. Drop the lock, wait a while and retry */
-		cfi_spin_unlock(chip->mutex);
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-		cfi_spin_lock(chip->mutex);
+		UDELAY(map, chip, adr, 1000000/HZ);
+	}
+	/* Did we succeed? */
+	if (!chip_good(map, adr, map_word_ff(map))) {
+		/* reset on all failures. */
+		map_write( map, CMD(0xF0), chip->start );
+		/* FIXME - should have reset delay before continuing */
+
+		ret = -EIO;
 	}
-	
-	printk(KERN_WARNING "MTD %s(): software timeout\n",
-	       __func__ );
-	
- op_failed:
-	/* reset on all failures. */
-	cfi_write( map, CMD(0xF0), chip->start );
-	/* FIXME - should have reset delay before continuing */
-	ret = -EIO;
 
- op_done:
 	chip->state = FL_READY;
 	put_chip(map, chip, adr);
-	cfi_spin_unlock(chip->mutex);
+	spin_unlock(chip->mutex);
 	return ret;
 }
 
@@ -1504,13 +1575,12 @@
 	ofs = instr->addr;
 	len = instr->len;
 
-	ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0);
+	ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
 	if (ret)
 		return ret;
 
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
@@ -1533,8 +1603,7 @@
 		return ret;
 
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
@@ -1553,7 +1622,7 @@
 		chip = &cfi->chips[i];
 
 	retry:
-		cfi_spin_lock(chip->mutex);
+		spin_lock(chip->mutex);
 
 		switch(chip->state) {
 		case FL_READY:
@@ -1567,14 +1636,14 @@
 			 * with the chip now anyway.
 			 */
 		case FL_SYNCING:
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 			break;
 
 		default:
 			/* Not an idle state */
 			add_wait_queue(&chip->wq, &wait);
 			
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 
 			schedule();
 
@@ -1589,13 +1658,13 @@
 	for (i--; i >=0; i--) {
 		chip = &cfi->chips[i];
 
-		cfi_spin_lock(chip->mutex);
+		spin_lock(chip->mutex);
 		
 		if (chip->state == FL_SYNCING) {
 			chip->state = chip->oldstate;
 			wake_up(&chip->wq);
 		}
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 	}
 }
 
@@ -1611,7 +1680,7 @@
 	for (i=0; !ret && i<cfi->numchips; i++) {
 		chip = &cfi->chips[i];
 
-		cfi_spin_lock(chip->mutex);
+		spin_lock(chip->mutex);
 
 		switch(chip->state) {
 		case FL_READY:
@@ -1631,7 +1700,7 @@
 			ret = -EAGAIN;
 			break;
 		}
-		cfi_spin_unlock(chip->mutex);
+		spin_unlock(chip->mutex);
 	}
 
 	/* Unlock the chips again */
@@ -1640,13 +1709,13 @@
 		for (i--; i >=0; i--) {
 			chip = &cfi->chips[i];
 
-			cfi_spin_lock(chip->mutex);
+			spin_lock(chip->mutex);
 		
 			if (chip->state == FL_PM_SUSPENDED) {
 				chip->state = chip->oldstate;
 				wake_up(&chip->wq);
 			}
-			cfi_spin_unlock(chip->mutex);
+			spin_unlock(chip->mutex);
 		}
 	}
 	
@@ -1665,151 +1734,20 @@
 	
 		chip = &cfi->chips[i];
 
-		cfi_spin_lock(chip->mutex);
+		spin_lock(chip->mutex);
 		
 		if (chip->state == FL_PM_SUSPENDED) {
 			chip->state = FL_READY;
-			cfi_write(map, CMD(0xF0), chip->start);
+			map_write(map, CMD(0xF0), chip->start);
 			wake_up(&chip->wq);
 		}
 		else
 			printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n");
 
-		cfi_spin_unlock(chip->mutex);
-	}
-}
-
-
-#ifdef DEBUG_LOCK_BITS
-
-static int do_printlockstatus_oneblock(struct map_info *map,
-				       struct flchip *chip,
-				       unsigned long adr,
-				       void *thunk)
-{
-	struct cfi_private *cfi = map->fldrv_priv;
-	int ofs_factor = cfi->interleave * cfi->device_type;
-
-	cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
-	printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
-	       adr, cfi_read_query(map, adr+(2*ofs_factor)));
-	cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL);
-	
-	return 0;
-}
-
-
-#define debug_dump_locks(mtd, frob, ofs, len, thunk) \
-	cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk))
-
-#else
-
-#define debug_dump_locks(...)
-
-#endif /* DEBUG_LOCK_BITS */
-
-
-struct xxlock_thunk {
-	cfi_word val;
-	flstate_t state;
-};
-
-
-#define DO_XXLOCK_ONEBLOCK_LOCK   ((struct xxlock_thunk){0x01, FL_LOCKING})
-#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING})
-
-
-/*
- * FIXME - this is *very* specific to a particular chip.  It likely won't
- * work for all chips that require unlock.  It also hasn't been tested
- * with interleaved chips.
- */
-static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
-{
-	struct cfi_private *cfi = map->fldrv_priv;
-	struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk;
-	int ret;
-
-	/*
-	 * This is easy because these are writes to registers and not writes
-	 * to flash memory - that means that we don't have to check status
-	 * and timeout.
-	 */
-
-	adr += chip->start;
-	/*
-	 * lock block registers:
-	 * - on 64k boundariesand
-	 * - bit 1 set high
-	 * - block lock registers are 4MiB lower - overflow subtract (danger)
-	 */
-	adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff;
-
-	cfi_spin_lock(chip->mutex);
-	ret = get_chip(map, chip, adr, FL_LOCKING);
-	if (ret) {
-		cfi_spin_unlock(chip->mutex);
-		return ret;
+		spin_unlock(chip->mutex);
 	}
-
-	chip->state = xxlt->state;
-	cfi_write(map, CMD(xxlt->val), adr);
-	
-	/* Done and happy. */
-	chip->state = FL_READY;
-	put_chip(map, chip, adr);
-	cfi_spin_unlock(chip->mutex);
-	return 0;
 }
 
-
-static int cfi_amdstd_lock_varsize(struct mtd_info *mtd,
-				   loff_t ofs,
-				   size_t len)
-{
-	int ret;
-
-	DEBUG(MTD_DEBUG_LEVEL3,
-	      "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
-	      __func__, ofs, len);
-	debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
-
-	ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
-				      (void *)&DO_XXLOCK_ONEBLOCK_LOCK);
-	
-	DEBUG(MTD_DEBUG_LEVEL3,
-	      "%s: lock status after, ret=%d\n",
-	      __func__, ret);
-
-	debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
-
-	return ret;
-}
-
-
-static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd,
-				     loff_t ofs,
-				     size_t len)
-{
-	int ret;
-
-	DEBUG(MTD_DEBUG_LEVEL3,
-	      "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
-	      __func__, ofs, len);
-	debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
-
-	ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
-				      (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK);
-	
-	DEBUG(MTD_DEBUG_LEVEL3,
-	      "%s: lock status after, ret=%d\n",
-	      __func__, ret);
-	debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
-	
-	return ret;
-}
-
-
 static void cfi_amdstd_destroy(struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
@@ -1823,7 +1761,7 @@
 static char im_name[]="cfi_cmdset_0002";
 
 
-int __init cfi_amdstd_init(void)
+static int __init cfi_amdstd_init(void)
 {
 	inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002);
 	return 0;
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/cfi_cmdset_0020.c linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0020.c
--- linux-2.4.30-orig/drivers/mtd/chips/cfi_cmdset_0020.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0020.c	2006-12-11 18:18:37.000000000 +0100
@@ -4,6 +4,7 @@
  *
  * (C) 2000 Red Hat. GPL'd
  *
+ * $Id: cfi_cmdset_0020.c,v 1.3 2005/10/12 14:47:50 lel Exp $
  * 
  * 10/10/2000	Nicolas Pitre <nico@cam.org>
  * 	- completely revamped method functions so they are aware and
@@ -11,12 +12,15 @@
  * 	- scalability vs code size is completely set at compile-time
  * 	  (see include/linux/mtd/cfi.h for selection)
  *	- optimized write buffer method
- * 06/21/2002	Joern Engel <joern@wh.fh-wedel.de> and others
+ * 06/21/2002	Jörn Engel <joern@wh.fh-wedel.de> and others
  *	- modified Intel Command Set 0x0001 to support ST Advanced Architecture
  *	  (command set 0x0020)
  *	- added a writev function
+ * 07/13/2005	Jörn Engel <joern@wh.fh-wedel.de>
+ * 	- Plugged memory leak in cfi_staa_writev().
  */
 
+#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -26,19 +30,18 @@
 #include <asm/byteorder.h>
 
 #include <linux/errno.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
-#include <linux/mtd/compatmac.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/mtd.h>
+#include <linux/mtd/compatmac.h>
 
 
 static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
-static int cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs,
+static int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs,
 		unsigned long count, loff_t to, size_t *retlen);
 static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *);
 static void cfi_staa_sync (struct mtd_info *);
@@ -130,6 +133,15 @@
 		if (!extp)
 			return NULL;
 
+		if (extp->MajorVersion != '1' ||
+		    (extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
+			printk(KERN_ERR "  Unknown ST Microelectronics"
+			       " Extended Query version %c.%c.\n",
+			       extp->MajorVersion, extp->MinorVersion);
+			kfree(extp);
+			return NULL;
+		}
+
 		/* Do some byteswapping if necessary */
 		extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport);
 		extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask);
@@ -238,7 +250,7 @@
 
 static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
 {
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo;
 	DECLARE_WAITQUEUE(wait, current);
 	int suspended = 0;
@@ -248,7 +260,7 @@
 	adr += chip->start;
 
 	/* Ensure cmd read/writes are aligned. */ 
-	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
+	cmd_addr = adr & ~(map_bankwidth(map)-1); 
 
 	/* Let's determine this according to the interleave only once */
 	status_OK = CMD(0x80);
@@ -265,30 +277,30 @@
 		if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2))
 			goto sleep; /* We don't support erase suspend */
 		
-		cfi_write (map, CMD(0xb0), cmd_addr);
+		map_write (map, CMD(0xb0), cmd_addr);
 		/* If the flash has finished erasing, then 'erase suspend'
 		 * appears to make some (28F320) flash devices switch to
 		 * 'read' mode.  Make sure that we switch to 'read status'
 		 * mode so we get the right data. --rmk
 		 */
-		cfi_write(map, CMD(0x70), cmd_addr);
+		map_write(map, CMD(0x70), cmd_addr);
 		chip->oldstate = FL_ERASING;
 		chip->state = FL_ERASE_SUSPENDING;
 		//		printk("Erase suspending at 0x%lx\n", cmd_addr);
 		for (;;) {
-			status = cfi_read(map, cmd_addr);
-			if ((status & status_OK) == status_OK)
+			status = map_read(map, cmd_addr);
+			if (map_word_andequal(map, status, status_OK, status_OK))
 				break;
 			
 			if (time_after(jiffies, timeo)) {
 				/* Urgh */
-				cfi_write(map, CMD(0xd0), cmd_addr);
+				map_write(map, CMD(0xd0), cmd_addr);
 				/* make sure we're in 'read status' mode */
-				cfi_write(map, CMD(0x70), cmd_addr);
+				map_write(map, CMD(0x70), cmd_addr);
 				chip->state = FL_ERASING;
 				spin_unlock_bh(chip->mutex);
 				printk(KERN_ERR "Chip not ready after erase "
-				       "suspended: status = 0x%x\n", status);
+				       "suspended: status = 0x%lx\n", status.x[0]);
 				return -EIO;
 			}
 			
@@ -298,7 +310,7 @@
 		}
 		
 		suspended = 1;
-		cfi_write(map, CMD(0xff), cmd_addr);
+		map_write(map, CMD(0xff), cmd_addr);
 		chip->state = FL_READY;
 		break;
 	
@@ -312,13 +324,13 @@
 
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
-		cfi_write(map, CMD(0x70), cmd_addr);
+		map_write(map, CMD(0x70), cmd_addr);
 		chip->state = FL_STATUS;
 
 	case FL_STATUS:
-		status = cfi_read(map, cmd_addr);
-		if ((status & status_OK) == status_OK) {
-			cfi_write(map, CMD(0xff), cmd_addr);
+		status = map_read(map, cmd_addr);
+		if (map_word_andequal(map, status, status_OK, status_OK)) {
+			map_write(map, CMD(0xff), cmd_addr);
 			chip->state = FL_READY;
 			break;
 		}
@@ -326,7 +338,7 @@
 		/* Urgh. Chip not yet ready to talk to us. */
 		if (time_after(jiffies, timeo)) {
 			spin_unlock_bh(chip->mutex);
-			printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %x\n", status);
+			printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]);
 			return -EIO;
 		}
 
@@ -361,8 +373,8 @@
 		   sending the 0x70 (Read Status) command to an erasing
 		   chip and expecting it to be ignored, that's what we 
 		   do. */
-		cfi_write(map, CMD(0xd0), cmd_addr);
-		cfi_write(map, CMD(0x70), cmd_addr);		
+		map_write(map, CMD(0xd0), cmd_addr);
+		map_write(map, CMD(0x70), cmd_addr);		
 	}
 
 	wake_up(&chip->wq);
@@ -413,16 +425,16 @@
 				  unsigned long adr, const u_char *buf, int len)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long cmd_adr, timeo;
 	DECLARE_WAITQUEUE(wait, current);
 	int wbufsize, z;
         
         /* M58LW064A requires bus alignment for buffer wriets -- saw */
-        if (adr & (CFIDEV_BUSWIDTH-1))
+        if (adr & (map_bankwidth(map)-1))
             return -EINVAL;
 
-        wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+        wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
         adr += chip->start;
 	cmd_adr = adr & ~(wbufsize-1);
 	
@@ -448,21 +460,21 @@
 		
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
-		cfi_write(map, CMD(0x70), cmd_adr);
+		map_write(map, CMD(0x70), cmd_adr);
                 chip->state = FL_STATUS;
 #ifdef DEBUG_CFI_FEATURES
-        printk("%s: 1 status[%x]\n", __FUNCTION__, cfi_read(map, cmd_adr));
+        printk("%s: 1 status[%x]\n", __FUNCTION__, map_read(map, cmd_adr));
 #endif
 
 	case FL_STATUS:
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		/* Urgh. Chip not yet ready to talk to us. */
 		if (time_after(jiffies, timeo)) {
 			spin_unlock_bh(chip->mutex);
-                        printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %x, status = %x\n",
-                               status, cfi_read(map, cmd_adr));
+                        printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n",
+                               status.x[0], map_read(map, cmd_adr).x[0]);
 			return -EIO;
 		}
 
@@ -484,13 +496,13 @@
 	}
 
 	ENABLE_VPP(map);
-	cfi_write(map, CMD(0xe8), cmd_adr);
+	map_write(map, CMD(0xe8), cmd_adr);
 	chip->state = FL_WRITING_TO_BUFFER;
 
 	z = 0;
 	for (;;) {
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 
 		spin_unlock_bh(chip->mutex);
@@ -500,32 +512,26 @@
 		if (++z > 100) {
 			/* Argh. Not ready for write to buffer */
 			DISABLE_VPP(map);
-                        cfi_write(map, CMD(0x70), cmd_adr);
+                        map_write(map, CMD(0x70), cmd_adr);
 			chip->state = FL_STATUS;
 			spin_unlock_bh(chip->mutex);
-			printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x\n", status);
+			printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]);
 			return -EIO;
 		}
 	}
 
 	/* Write length of data to come */
-	cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr );
+	map_write(map, CMD(len/map_bankwidth(map)-1), cmd_adr );
         
 	/* Write data */
-	for (z = 0; z < len; z += CFIDEV_BUSWIDTH) {
-		if (cfi_buswidth_is_1()) {
-			map_write8 (map, *((__u8*)buf)++, adr+z);
-		} else if (cfi_buswidth_is_2()) {
-			map_write16 (map, *((__u16*)buf)++, adr+z);
-		} else if (cfi_buswidth_is_4()) {
-			map_write32 (map, *((__u32*)buf)++, adr+z);
-		} else {
-			DISABLE_VPP(map);
-			return -EINVAL;
-		}
+	for (z = 0; z < len;
+	     z += map_bankwidth(map), buf += map_bankwidth(map)) {
+		map_word d;
+		d = map_word_load(map, buf);
+		map_write(map, d, adr+z);
 	}
 	/* GO GO GO */
-	cfi_write(map, CMD(0xd0), cmd_adr);
+	map_write(map, CMD(0xd0), cmd_adr);
 	chip->state = FL_WRITING;
 
 	spin_unlock_bh(chip->mutex);
@@ -547,16 +553,16 @@
 			continue;
 		}
 
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
                         /* clear status */
-                        cfi_write(map, CMD(0x50), cmd_adr);
+                        map_write(map, CMD(0x50), cmd_adr);
                         /* put back into read status register mode */
-                        cfi_write(map, CMD(0x70), adr);
+                        map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
 			DISABLE_VPP(map);
 			spin_unlock_bh(chip->mutex);
@@ -583,19 +589,18 @@
 	chip->state = FL_STATUS;
 
         /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */
-        if ((status & CMD(0x02)) || (status & CMD(0x08)) ||
-            (status & CMD(0x10)) || (status & CMD(0x20))) {
+        if (map_word_bitsset(map, status, CMD(0x3a))) {
 #ifdef DEBUG_CFI_FEATURES
-            printk("%s: 2 status[%x]\n", __FUNCTION__, status);
+		printk("%s: 2 status[%lx]\n", __FUNCTION__, status.x[0]);
 #endif
-            /* clear status */
-            cfi_write(map, CMD(0x50), cmd_adr);
-            /* put back into read status register mode */
-            cfi_write(map, CMD(0x70), adr);
-            wake_up(&chip->wq);
-            spin_unlock_bh(chip->mutex);
-            return (status & CMD(0x02)) ? -EROFS : -EIO;
-        }
+		/* clear status */
+		map_write(map, CMD(0x50), cmd_adr);
+		/* put back into read status register mode */
+		map_write(map, CMD(0x70), adr);
+		wake_up(&chip->wq);
+		spin_unlock_bh(chip->mutex);
+		return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO;
+	}
 	wake_up(&chip->wq);
 	spin_unlock_bh(chip->mutex);
 
@@ -607,7 +612,7 @@
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
 	int ret = 0;
 	int chipnum;
 	unsigned long ofs;
@@ -620,7 +625,7 @@
 	ofs = to  - (chipnum << cfi->chipshift);
 
 #ifdef DEBUG_CFI_FEATURES
-        printk("%s: CFIDEV_BUSWIDTH[%x]\n", __FUNCTION__, CFIDEV_BUSWIDTH);
+        printk("%s: map_bankwidth(map)[%x]\n", __FUNCTION__, map_bankwidth(map));
         printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize);
         printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len);
 #endif
@@ -663,7 +668,7 @@
 #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1))
 #define ECCBUF_MOD(x) ((x) &  (ECCBUF_SIZE - 1))
 static int
-cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs,
+cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs,
 		unsigned long count, loff_t to, size_t *retlen)
 {
 	unsigned long i;
@@ -725,6 +730,7 @@
 write_error:
 	if (retlen)
 		*retlen = totlen;
+	kfree(buffer);
 	return ret;
 }
 
@@ -732,7 +738,7 @@
 static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo;
 	int retries = 3;
 	DECLARE_WAITQUEUE(wait, current);
@@ -752,12 +758,12 @@
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
 	case FL_READY:
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
 		chip->state = FL_STATUS;
 
 	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* Urgh. Chip not yet ready to talk to us. */
@@ -786,15 +792,15 @@
 
 	ENABLE_VPP(map);
 	/* Clear the status register first */
-	cfi_write(map, CMD(0x50), adr);
+	map_write(map, CMD(0x50), adr);
 
 	/* Now erase */
-	cfi_write(map, CMD(0x20), adr);
-	cfi_write(map, CMD(0xD0), adr);
+	map_write(map, CMD(0x20), adr);
+	map_write(map, CMD(0xD0), adr);
 	chip->state = FL_ERASING;
 	
 	spin_unlock_bh(chip->mutex);
-	schedule_timeout(HZ);
+	msleep(1000);
 	spin_lock_bh(chip->mutex);
 
 	/* FIXME. Use a timer to check this, and return immediately. */
@@ -814,15 +820,15 @@
 			continue;
 		}
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr));
+			printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]);
 			DISABLE_VPP(map);
 			spin_unlock_bh(chip->mutex);
 			return -EIO;
@@ -838,43 +844,46 @@
 	ret = 0;
 
 	/* We've broken this before. It doesn't hurt to be safe */
-	cfi_write(map, CMD(0x70), adr);
+	map_write(map, CMD(0x70), adr);
 	chip->state = FL_STATUS;
-	status = cfi_read(map, adr);
+	status = map_read(map, adr);
 
 	/* check for lock bit */
-	if (status & CMD(0x3a)) {
-		unsigned char chipstatus = status;
-		if (status != CMD(status & 0xff)) {
-			int i;
-			for (i = 1; i<CFIDEV_INTERLEAVE; i++) {
-				      chipstatus |= status >> (cfi->device_type * 8);
+	if (map_word_bitsset(map, status, CMD(0x3a))) {
+		unsigned char chipstatus = status.x[0];
+		if (!map_word_equal(map, status, CMD(chipstatus))) {
+			int i, w;
+			for (w=0; w<map_words(map); w++) {
+				for (i = 0; i<cfi_interleave(cfi); i++) {
+					chipstatus |= status.x[w] >> (cfi->device_type * 8);
+				}
 			}
-			printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus);
+			printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n",
+			       status.x[0], chipstatus);
 		}
 		/* Reset the error bits */
-		cfi_write(map, CMD(0x50), adr);
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x50), adr);
+		map_write(map, CMD(0x70), adr);
 		
 		if ((chipstatus & 0x30) == 0x30) {
-			printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status);
+			printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus);
 			ret = -EIO;
 		} else if (chipstatus & 0x02) {
 			/* Protection bit set */
 			ret = -EROFS;
 		} else if (chipstatus & 0x8) {
 			/* Voltage */
-			printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", status);
+			printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus);
 			ret = -EIO;
 		} else if (chipstatus & 0x20) {
 			if (retries--) {
-				printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status);
+				printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus);
 				timeo = jiffies + HZ;
 				chip->state = FL_STATUS;
 				spin_unlock_bh(chip->mutex);
 				goto retry;
 			}
-			printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, status);
+			printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus);
 			ret = -EIO;
 		}
 	}
@@ -969,8 +978,7 @@
 	}
 		
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
@@ -1035,7 +1043,7 @@
 static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo = jiffies + HZ;
 	DECLARE_WAITQUEUE(wait, current);
 
@@ -1053,12 +1061,12 @@
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
 	case FL_READY:
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
 		chip->state = FL_STATUS;
 
 	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK) 
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK)) 
 			break;
 		
 		/* Urgh. Chip not yet ready to talk to us. */
@@ -1086,12 +1094,12 @@
 	}
 
 	ENABLE_VPP(map);
-	cfi_write(map, CMD(0x60), adr);
-	cfi_write(map, CMD(0x01), adr);
+	map_write(map, CMD(0x60), adr);
+	map_write(map, CMD(0x01), adr);
 	chip->state = FL_LOCKING;
 	
 	spin_unlock_bh(chip->mutex);
-	schedule_timeout(HZ);
+	msleep(1000);
 	spin_lock_bh(chip->mutex);
 
 	/* FIXME. Use a timer to check this, and return immediately. */
@@ -1100,15 +1108,15 @@
 	timeo = jiffies + (HZ*2);
 	for (;;) {
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr));
+			printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]);
 			DISABLE_VPP(map);
 			spin_unlock_bh(chip->mutex);
 			return -EIO;
@@ -1184,7 +1192,7 @@
 static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo = jiffies + HZ;
 	DECLARE_WAITQUEUE(wait, current);
 
@@ -1202,12 +1210,12 @@
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
 	case FL_READY:
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
 		chip->state = FL_STATUS;
 
 	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* Urgh. Chip not yet ready to talk to us. */
@@ -1235,12 +1243,12 @@
 	}
 
 	ENABLE_VPP(map);
-	cfi_write(map, CMD(0x60), adr);
-	cfi_write(map, CMD(0xD0), adr);
+	map_write(map, CMD(0x60), adr);
+	map_write(map, CMD(0xD0), adr);
 	chip->state = FL_UNLOCKING;
 	
 	spin_unlock_bh(chip->mutex);
-	schedule_timeout(HZ);
+	msleep(1000);
 	spin_lock_bh(chip->mutex);
 
 	/* FIXME. Use a timer to check this, and return immediately. */
@@ -1249,15 +1257,15 @@
 	timeo = jiffies + (HZ*2);
 	for (;;) {
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr));
+			printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]);
 			DISABLE_VPP(map);
 			spin_unlock_bh(chip->mutex);
 			return -EIO;
@@ -1386,7 +1394,7 @@
 		
 		/* Go to known state. Chip may have been power cycled */
 		if (chip->state == FL_PM_SUSPENDED) {
-			cfi_write(map, CMD(0xFF), 0);
+			map_write(map, CMD(0xFF), 0);
 			chip->state = FL_READY;
 			wake_up(&chip->wq);
 		}
@@ -1403,14 +1411,9 @@
 	kfree(cfi);
 }
 
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define cfi_staa_init init_module
-#define cfi_staa_exit cleanup_module
-#endif
-
 static char im_name[]="cfi_cmdset_0020";
 
-int __init cfi_staa_init(void)
+static int __init cfi_staa_init(void)
 {
 	inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020);
 	return 0;
@@ -1423,3 +1426,5 @@
 
 module_init(cfi_staa_init);
 module_exit(cfi_staa_exit);
+
+MODULE_LICENSE("GPL");
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/cfi_probe.c linux-2.4.30/drivers/mtd/chips/cfi_probe.c
--- linux-2.4.30-orig/drivers/mtd/chips/cfi_probe.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/cfi_probe.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,7 +1,7 @@
 /* 
    Common Flash Interface probe code.
    (C) 2000 Red Hat. GPL'd.
-   $Id: cfi_probe.c,v 1.74 2004/02/17 16:43:41 dvrabel Exp $
+   $Id: cfi_probe.c,v 1.3 2005/10/12 14:47:51 lel Exp $
 */
 
 #include <linux/config.h>
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 
+#include <linux/mtd/xip.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/gen_probe.h>
@@ -31,25 +32,75 @@
 
 struct mtd_info *cfi_probe(struct map_info *map);
 
+#ifdef CONFIG_MTD_XIP
+
+/* only needed for short periods, so this is rather simple */
+#define xip_disable()	local_irq_disable()
+
+#define xip_allowed(base, map) \
+do { \
+	(void) map_read(map, base); \
+	asm volatile (".rep 8; nop; .endr"); \
+	local_irq_enable(); \
+} while (0)
+
+#define xip_enable(base, map, cfi) \
+do { \
+	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \
+	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \
+	xip_allowed(base, map); \
+} while (0)
+
+#define xip_disable_qry(base, map, cfi) \
+do { \
+	xip_disable(); \
+	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \
+	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \
+	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \
+} while (0)
+
+#else
+
+#define xip_disable()			do { } while (0)
+#define xip_allowed(base, map)		do { } while (0)
+#define xip_enable(base, map, cfi)	do { } while (0)
+#define xip_disable_qry(base, map, cfi) do { } while (0)
+
+#endif
+
 /* check for QRY.
    in: interleave,type,mode
    ret: table index, <0 for error
  */
-static inline int qry_present(struct map_info *map, __u32 base,
+static int __xipram qry_present(struct map_info *map, __u32 base,
 				struct cfi_private *cfi)
 {
 	int osf = cfi->interleave * cfi->device_type;	// scale factor
+	map_word val[3];
+	map_word qry[3];
+
+	qry[0] = cfi_build_cmd('Q', map, cfi);
+	qry[1] = cfi_build_cmd('R', map, cfi);
+	qry[2] = cfi_build_cmd('Y', map, cfi);
+
+	val[0] = map_read(map, base + osf*0x10);
+	val[1] = map_read(map, base + osf*0x11);
+	val[2] = map_read(map, base + osf*0x12);
 
-	if (cfi_read(map,base+osf*0x10)==cfi_build_cmd('Q',map,cfi) &&
-	    cfi_read(map,base+osf*0x11)==cfi_build_cmd('R',map,cfi) &&
-	    cfi_read(map,base+osf*0x12)==cfi_build_cmd('Y',map,cfi))
-		return 1;	// ok !
+	if (!map_word_equal(map, qry[0], val[0]))
+		return 0;
 
-	return 0; 	// nothing found
+	if (!map_word_equal(map, qry[1], val[1]))
+		return 0;
+
+	if (!map_word_equal(map, qry[2], val[2]))
+		return 0;
+
+	return 1; 	// "QRY" found
 }
 
-static int cfi_probe_chip(struct map_info *map, __u32 base,
-			  unsigned long *chip_map, struct cfi_private *cfi)
+static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,
+				   unsigned long *chip_map, struct cfi_private *cfi)
 {
 	int i;
 	
@@ -65,11 +116,16 @@
 			(unsigned long)base + 0x55, map->size -1);
 		return 0;
 	}
+
+	xip_disable();
 	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
 	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
 
-	if (!qry_present(map,base,cfi))
+	if (!qry_present(map,base,cfi)) {
+		xip_enable(base, map, cfi);
 		return 0;
+	}
 
 	if (!cfi->numchips) {
 		/* This is the first time we're called. Set up the CFI 
@@ -91,9 +147,11 @@
 			/* Eep. This chip also had the QRY marker. 
 			 * Is it an alias for the new one? */
 			cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL);
+			cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
 
 			/* If the QRY marker goes away, it's an alias */
 			if (!qry_present(map, start, cfi)) {
+				xip_allowed(base, map);
 				printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
 				       map->name, base, start);
 				return 0;
@@ -103,8 +161,10 @@
 			 * too and if it's the same, assume it's an alias. */
 			/* FIXME: Use other modes to do a proper check */
 			cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+			cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
 			
 			if (qry_present(map, base, cfi)) {
+				xip_allowed(base, map);
 				printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
 				       map->name, base, start);
 				return 0;
@@ -119,22 +179,25 @@
 	
 	/* Put it back into Read Mode */
 	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
+	xip_allowed(base, map);
 
-	printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n",
+	printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
 	       map->name, cfi->interleave, cfi->device_type*8, base,
-	       map->buswidth*8);
+	       map->bankwidth*8);
 	
 	return 1;
 }
 
-static int cfi_chip_setup(struct map_info *map, 
-		   struct cfi_private *cfi)
+static int __xipram cfi_chip_setup(struct map_info *map, 
+				   struct cfi_private *cfi)
 {
 	int ofs_factor = cfi->interleave*cfi->device_type;
 	__u32 base = 0;
 	int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor);
 	int i;
 
+	xip_enable(base, map, cfi);
 #ifdef DEBUG_CFI
 	printk("Number of erase regions: %d\n", num_erase_regions);
 #endif
@@ -152,13 +215,33 @@
 	cfi->cfi_mode = CFI_MODE_CFI;
 	
 	/* Read the CFI info structure */
-	for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) {
+	xip_disable_qry(base, map, cfi);
+	for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++)
 		((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor);
-	}
-	
+
+	/* Note we put the device back into Read Mode BEFORE going into Auto
+	 * Select Mode, as some devices support nesting of modes, others
+	 * don't. This way should always work.
+	 * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and
+	 * so should be treated as nops or illegal (and so put the device
+	 * back into Read Mode, which is a nop in this case).
+	 */
+	cfi_send_gen_cmd(0xf0,     0, base, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
+	cfi->mfr = cfi_read_query(map, base);
+	cfi->id = cfi_read_query(map, base + ofs_factor);    
+
+	/* Put it back into Read Mode */
+	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+	/* ... even if it's an Intel chip */
+	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
+	xip_allowed(base, map);
+
 	/* Do any necessary byteswapping */
 	cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID);
-	
+
 	cfi->cfiq->P_ADR = le16_to_cpu(cfi->cfiq->P_ADR);
 	cfi->cfiq->A_ID = le16_to_cpu(cfi->cfiq->A_ID);
 	cfi->cfiq->A_ADR = le16_to_cpu(cfi->cfiq->A_ADR);
@@ -180,26 +263,9 @@
 #endif
 	}
 
-	/* Note we put the device back into Read Mode BEFORE going into Auto
-	 * Select Mode, as some devices support nesting of modes, others
-	 * don't. This way should always work.
-	 * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and
-	 * so should be treated as nops or illegal (and so put the device
-	 * back into Read Mode, which is a nop in this case).
-	 */
-	cfi_send_gen_cmd(0xf0,     0, base, map, cfi, cfi->device_type, NULL);
-	cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
-	cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
-	cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
-	cfi->mfr = cfi_read_query(map, base);
-	cfi->id = cfi_read_query(map, base + ofs_factor);    
-
-	/* Put it back into Read Mode */
-	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
-
-	printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n",
+	printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
 	       map->name, cfi->interleave, cfi->device_type*8, base,
-	       map->buswidth*8);
+	       map->bankwidth*8);
 
 	return 1;
 }
@@ -222,12 +288,27 @@
 		
 	case P_ID_AMD_EXT:
 		return "AMD/Fujitsu Extended";
+
+	case P_ID_WINBOND:
+		return "Winbond Standard";
 		
+	case P_ID_ST_ADV:
+		return "ST Advanced";
+
 	case P_ID_MITSUBISHI_STD:
 		return "Mitsubishi Standard";
 		
 	case P_ID_MITSUBISHI_EXT:
 		return "Mitsubishi Extended";
+
+	case P_ID_SST_PAGE:
+		return "SST Page Write";
+
+	case P_ID_INTEL_PERFORMANCE:
+		return "Intel Performance Code";
+		
+	case P_ID_INTEL_DATA:
+		return "Intel Data";
 		
 	case P_ID_RESERVED:
 		return "Not Allowed / Reserved for Future Use";
@@ -306,6 +387,10 @@
 		printk("  - x32-only asynchronous interface\n");
 		break;
 		
+	case 4:
+		printk("  - supports x16 and x32 via Word# with asynchronous interface\n");
+		break;
+		
 	case 65535:
 		printk("  - Not Allowed / Reserved\n");
 		break;
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/cfi_util.c linux-2.4.30/drivers/mtd/chips/cfi_util.c
--- linux-2.4.30-orig/drivers/mtd/chips/cfi_util.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/cfi_util.c	2006-12-11 18:18:37.000000000 +0100
@@ -7,7 +7,7 @@
  *
  * This code is covered by the GPL.
  *
- * $Id: cfi_util.c,v 1.3 2003/11/14 19:50:03 thayne Exp $
+ * $Id: cfi_util.c,v 1.3 2005/10/12 14:47:51 lel Exp $
  *
  */
 
@@ -22,12 +22,14 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/mtd/xip.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/compatmac.h>
 
 struct cfi_extquery *
-cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
+__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	__u32 base = 0; // cfi->chips[0].start;
@@ -39,53 +41,147 @@
 	if (!adr)
 		goto out;
 
-	/* Switch it into Query Mode */
-	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
-
 	extp = kmalloc(size, GFP_KERNEL);
 	if (!extp) {
 		printk(KERN_ERR "Failed to allocate memory\n");
 		goto out;
 	}
-		
+
+#ifdef CONFIG_MTD_XIP
+	local_irq_disable();
+#endif
+
+	/* Switch it into Query Mode */
+	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+
 	/* Read in the Extended Query Table */
 	for (i=0; i<size; i++) {
 		((unsigned char *)extp)[i] = 
 			cfi_read_query(map, base+((adr+i)*ofs_factor));
 	}
 
-	if (extp->MajorVersion != '1' || 
-	    (extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
-		printk(KERN_WARNING "  Unknown %s Extended Query "
-		       "version %c.%c.\n",  name, extp->MajorVersion,
-		       extp->MinorVersion);
-		kfree(extp);
-		extp = NULL;
-		goto out;
-	}
-
-out:
-	/* Make sure it's in read mode */
+	/* Make sure it returns to read mode */
 	cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL);
+
+#ifdef CONFIG_MTD_XIP
+	(void) map_read(map, base);
+	asm volatile (".rep 8; nop; .endr");
+	local_irq_enable();
+#endif
 
-	return extp;
+ out:	return extp;
 }
 
 EXPORT_SYMBOL(cfi_read_pri);
 
-void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups)
+void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	struct cfi_fixup *f;
 
 	for (f=fixups; f->fixup; f++) {
 		if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
 		    ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
-			f->fixup(map, f->param);
+			f->fixup(mtd, f->param);
 		}
 	}
 }
 
 EXPORT_SYMBOL(cfi_fixup);
 
+int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
+				     loff_t ofs, size_t len, void *thunk)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	unsigned long adr;
+	int chipnum, ret = 0;
+	int i, first;
+	struct mtd_erase_region_info *regions = mtd->eraseregions;
+
+	if (ofs > mtd->size)
+		return -EINVAL;
+
+	if ((len + ofs) > 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 && ofs >= 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 (ofs & (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 && (ofs + len) >= regions[i].offset)
+		i++;
+
+	/* As before, drop back one to point at the region in which
+	   the address actually falls
+	*/
+	i--;
+	
+	if ((ofs + len) & (regions[i].erasesize-1))
+		return -EINVAL;
+
+	chipnum = ofs >> cfi->chipshift;
+	adr = ofs - (chipnum << cfi->chipshift);
+
+	i=first;
+
+	while(len) {
+		int size = regions[i].erasesize;
+
+		ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
+		
+		if (ret)
+			return ret;
+
+		adr += size;
+		ofs += size;
+		len -= size;
+
+		if (ofs == regions[i].offset + size * regions[i].numblocks)
+			i++;
+
+		if (adr >> cfi->chipshift) {
+			adr = 0;
+			chipnum++;
+			
+			if (chipnum >= cfi->numchips)
+			break;
+		}
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL(cfi_varsize_frob);
+
 MODULE_LICENSE("GPL");
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/chipreg.c linux-2.4.30/drivers/mtd/chips/chipreg.c
--- linux-2.4.30-orig/drivers/mtd/chips/chipreg.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/chipreg.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: chipreg.c,v 1.16 2003/05/29 09:36:15 dwmw2 Exp $
+ * $Id: chipreg.c,v 1.3 2005/10/12 14:47:51 lel Exp $
  *
  * Registration for chip drivers
  *
@@ -15,7 +15,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/compatmac.h>
 
-spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(chip_drvs_lock);
 static LIST_HEAD(chip_drvs_list);
 
 void register_mtd_chip_driver(struct mtd_chip_driver *drv)
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/fwh_lock.h linux-2.4.30/drivers/mtd/chips/fwh_lock.h
--- linux-2.4.30-orig/drivers/mtd/chips/fwh_lock.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/chips/fwh_lock.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,107 @@
+#ifndef FWH_LOCK_H
+#define FWH_LOCK_H
+
+
+enum fwh_lock_state {
+        FWH_UNLOCKED   = 0,
+	FWH_DENY_WRITE = 1,
+	FWH_IMMUTABLE  = 2,
+	FWH_DENY_READ  = 4,
+};
+
+struct fwh_xxlock_thunk {
+	enum fwh_lock_state val;
+	flstate_t state;
+};
+
+
+#define FWH_XXLOCK_ONEBLOCK_LOCK   ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING})
+#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED,   FL_UNLOCKING})
+
+/*
+ * This locking/unlock is specific to firmware hub parts.  Only one
+ * is known that supports the Intel command set.    Firmware
+ * hub parts cannot be interleaved as they are on the LPC bus
+ * so this code has not been tested with interleaved chips,
+ * and will likely fail in that context.
+ */
+static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, 
+	unsigned long adr, int len, void *thunk)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk;
+	int ret;
+
+	/* Refuse the operation if the we cannot look behind the chip */
+	if (chip->start < 0x400000) {
+		DEBUG( MTD_DEBUG_LEVEL3,
+			"MTD %s(): chip->start: %lx wanted >= 0x400000\n",
+			__func__, chip->start );
+		return -EIO;
+	}
+	/*
+	 * lock block registers:
+	 * - on 64k boundariesand
+	 * - bit 1 set high
+	 * - block lock registers are 4MiB lower - overflow subtract (danger)
+	 * 
+	 * The address manipulation is first done on the logical address
+	 * which is 0 at the start of the chip, and then the offset of
+	 * the individual chip is addted to it.  Any other order a weird
+	 * map offset could cause problems.
+	 */
+	adr = (adr & ~0xffffUL) | 0x2;
+	adr += chip->start - 0x400000;
+
+	/*
+	 * This is easy because these are writes to registers and not writes
+	 * to flash memory - that means that we don't have to check status
+	 * and timeout.
+	 */
+	spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_LOCKING);
+	if (ret) {
+		spin_unlock(chip->mutex);
+		return ret;
+	}
+
+	chip->state = xxlt->state;
+	map_write(map, CMD(xxlt->val), adr);
+
+	/* Done and happy. */
+	chip->state = FL_READY;
+	put_chip(map, chip, adr);
+	spin_unlock(chip->mutex);
+	return 0;
+}
+
+
+static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+	int ret;
+
+	ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
+		(void *)&FWH_XXLOCK_ONEBLOCK_LOCK);
+
+	return ret;
+}
+
+
+static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+	int ret;
+
+	ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
+		(void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK);
+	
+	return ret;
+}
+
+static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param)
+{
+	printk(KERN_NOTICE "using fwh lock/unlock method\n");
+	/* Setup for the chips with the fwh lock method */
+	mtd->lock   = fwh_lock_varsize;
+	mtd->unlock = fwh_unlock_varsize;
+}
+#endif /* FWH_LOCK_H */
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/chips/gen_probe.c linux-2.4.30/drivers/mtd/chips/gen_probe.c
--- linux-2.4.30-orig/drivers/mtd/chips/gen_probe.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/chips/gen_probe.c	2006-12-11 18:18:37.000000000 +0100
@@ -2,7 +2,7 @@
  * Routines common to all CFI-type probes.
  * (C) 2001-2003 Red Hat, Inc.
  * GPL'd
- * $Id: gen_probe.c,v 1.14 2003/11/08 00:51:21 dsaxena Exp $
+ * $Id: gen_probe.c,v 1.3 2005/10/12 14:47:51 lel Exp $
  */
 
 #include <linux/kernel.h>
@@ -11,7 +11,6 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/cfi.h>
-#include <linux/mtd/mtd.h>
 #include <linux/mtd/gen_probe.h>
 
 static struct mtd_info *check_cmd_set(struct map_info *, int);
@@ -51,12 +50,12 @@
 EXPORT_SYMBOL(mtd_do_chip_probe);
 
 
-struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
+static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
 {
 	struct cfi_private cfi;
 	struct cfi_private *retcfi;
 	unsigned long *chip_map;
-	int i, j;
+	int i, j, mapsize;
 	int max_chips;
 
 	memset(&cfi, 0, sizeof(cfi));
@@ -65,7 +64,7 @@
 	   interleave and device type, etc. */
 	if (!genprobe_new_chip(map, cp, &cfi)) {
 		/* The probe didn't like it */
-		printk(KERN_WARNING "%s: Found no %s device at location zero\n",
+		printk(KERN_DEBUG "%s: Found no %s device at location zero\n",
 		       cp->name, map->name);
 		return NULL;
 	}		
@@ -82,22 +81,15 @@
 #endif
 	cfi.chipshift = cfi.cfiq->DevSize;
 
-	switch(cfi.interleave) {
-#ifdef CFIDEV_INTERLEAVE_1
-	case 1:
-		break;
-#endif
-#ifdef CFIDEV_INTERLEAVE_2
-	case 2:
+	if (cfi_interleave_is_1(&cfi)) {
+		;
+	} else if (cfi_interleave_is_2(&cfi)) {
 		cfi.chipshift++;
-		break;
-#endif
-#ifdef CFIDEV_INTERLEAVE_4
-	case 4:
-		cfi.chipshift+=2;
-		break;
-#endif
-	default:
+	} else if (cfi_interleave_is_4((&cfi))) {
+		cfi.chipshift += 2;
+	} else if (cfi_interleave_is_8(&cfi)) {
+		cfi.chipshift += 3;
+	} else {
 		BUG();
 	}
 		
@@ -108,12 +100,14 @@
 	 * Align bitmap storage size to full byte. 
 	 */ 
 	max_chips = map->size >> cfi.chipshift;
-	chip_map = kmalloc((max_chips / 8) + ((max_chips % 8) ? 1 : 0), GFP_KERNEL);
+	mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0);
+	chip_map = kmalloc(mapsize, GFP_KERNEL);
 	if (!chip_map) {
 		printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
 		kfree(cfi.cfiq);
 		return NULL;
 	}
+	memset (chip_map, 0, mapsize);
 
 	set_bit(0, chip_map); /* Mark first chip valid */
 
@@ -164,131 +158,31 @@
 static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
 			     struct cfi_private *cfi)
 {
-	switch (map->buswidth) {
-#ifdef CFIDEV_BUSWIDTH_1		
-	case CFIDEV_BUSWIDTH_1:
-		cfi->interleave = CFIDEV_INTERLEAVE_1;
-
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-		break;			
-#endif /* CFIDEV_BUSWITDH_1 */
-
-#ifdef CFIDEV_BUSWIDTH_2		
-	case CFIDEV_BUSWIDTH_2:
-#ifdef CFIDEV_INTERLEAVE_1
-		cfi->interleave = CFIDEV_INTERLEAVE_1;
-
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_1 */
-#ifdef CFIDEV_INTERLEAVE_2
-		cfi->interleave = CFIDEV_INTERLEAVE_2;
-
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_2 */
-		break;			
-#endif /* CFIDEV_BUSWIDTH_2 */
-
-#ifdef CFIDEV_BUSWIDTH_4
-	case CFIDEV_BUSWIDTH_4:
-#if defined(CFIDEV_INTERLEAVE_1) && defined(SOMEONE_ACTUALLY_MAKES_THESE)
-                cfi->interleave = CFIDEV_INTERLEAVE_1;
-
-                cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_1 */
-#ifdef CFIDEV_INTERLEAVE_2
-		cfi->interleave = CFIDEV_INTERLEAVE_2;
-
-#ifdef SOMEONE_ACTUALLY_MAKES_THESE
-		cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_2 */
-#ifdef CFIDEV_INTERLEAVE_4
-		cfi->interleave = CFIDEV_INTERLEAVE_4;
-
-#ifdef SOMEONE_ACTUALLY_MAKES_THESE
-		cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_4 */
-		break;
-#endif /* CFIDEV_BUSWIDTH_4 */
-
-#ifdef CFIDEV_BUSWIDTH_8
-	case CFIDEV_BUSWIDTH_8:
-#if defined(CFIDEV_INTERLEAVE_2) && defined(SOMEONE_ACTUALLY_MAKES_THESE)
-                cfi->interleave = CFIDEV_INTERLEAVE_2;
-
-                cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_2 */
-#ifdef CFIDEV_INTERLEAVE_4
-		cfi->interleave = CFIDEV_INTERLEAVE_4;
-
-#ifdef SOMEONE_ACTUALLY_MAKES_THESE
-		cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_4 */
-#ifdef CFIDEV_INTERLEAVE_8
-		cfi->interleave = CFIDEV_INTERLEAVE_8;
-
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_8 */
-		break;
-#endif /* CFIDEV_BUSWIDTH_8 */
-
-	default:
-		printk(KERN_WARNING "genprobe_new_chip called with unsupported buswidth %d\n", map->buswidth);
-		return 0;
+	int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */
+	int max_chips = map_bankwidth(map); /* And minimum 1 */
+	int nr_chips, type;
+
+	for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) {
+
+		if (!cfi_interleave_supported(nr_chips))
+		    continue;
+
+		cfi->interleave = nr_chips;
+
+		/* Minimum device size. Don't look for one 8-bit device
+		   in a 16-bit bus, etc. */
+		type = map_bankwidth(map) / nr_chips;
+
+		for (; type <= CFI_DEVICETYPE_X32; type<<=1) {
+			cfi->device_type = type;
+
+			if (cp->probe_chip(map, 0, NULL, cfi))
+				return 1;
+		}
 	}
 	return 0;
 }
 
-
 typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int);
 
 extern cfi_cmdset_fn_t cfi_cmdset_0001;
@@ -341,6 +235,7 @@
 #ifdef CONFIG_MTD_CFI_INTELEXT
 	case 0x0001:
 	case 0x0003:
+	case 0x0200:
 		return cfi_cmdset_0001(map, primary);
 #endif
 #ifdef CONFIG_MTD_CFI_AMDSTD
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/cmdlinepart.c linux-2.4.30/drivers/mtd/cmdlinepart.c
--- linux-2.4.30-orig/drivers/mtd/cmdlinepart.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/cmdlinepart.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: cmdlinepart.c,v 1.12 2004/03/18 08:43:14 dwmw2 Exp $
+ * $Id: cmdlinepart.c,v 1.2 2005/09/28 14:18:54 lel Exp $
  *
  * Read flash partition table from command line
  *
@@ -28,7 +28,6 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
-#include <linux/mtd/compatmac.h>
 #include <linux/bootmem.h>
 
 /* error message prefix */
@@ -95,7 +94,7 @@
 		if (size < PAGE_SIZE)
 		{
 			printk(KERN_ERR ERRP "partition size too small (%lx)\n", size);
-			return 0;
+			return NULL;
 		}
 	}
 
@@ -122,7 +121,7 @@
 		if ((p = strchr(name, delim)) == 0)
 		{
 			printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim);
-			return 0;
+			return NULL;
 		}
 		name_len = p - name;
 		s = p + 1;
@@ -149,12 +148,12 @@
 		if (size == SIZE_REMAINING)
 		{
 			printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n");
-			return 0;
+			return NULL;
 		}
 		/* more partitions follow, parse them */
 		if ((parts = newpart(s + 1, &s, num_parts, 
 		                     this_part + 1, &extra_mem, extra_mem_size)) == 0)
-		  return 0;
+		  return NULL;
 	}
 	else
 	{	/* this is the last partition: allocate space for all */
@@ -167,7 +166,7 @@
 		if (!parts)
 		{
 			printk(KERN_ERR ERRP "out of memory\n");
-			return 0;
+			return NULL;
 		}
 		memset(parts, 0, alloc_size);
 		extra_mem = (unsigned char *)(parts + *num_parts);
@@ -339,8 +338,10 @@
  * This is the handler for our kernel parameter, called from 
  * main.c::checksetup(). Note that we can not yet kmalloc() anything,
  * so we only save the commandline for later processing.
+ *
+ * This function needs to be visible for bootloaders.
  */
-static int __init mtdpart_setup(char *s)
+int mtdpart_setup(char *s)
 {
 	cmdline = s;
 	return 1;
@@ -359,14 +360,7 @@
 	return register_mtd_parser(&cmdline_parser);
 }
 
-static void __exit cmdline_parser_exit(void)
-{
-	deregister_mtd_parser(&cmdline_parser);
-}
-
 module_init(cmdline_parser_init);
-module_exit(cmdline_parser_exit);
-
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/maps/Config.in linux-2.4.30/drivers/mtd/maps/Config.in
--- linux-2.4.30-orig/drivers/mtd/maps/Config.in	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/drivers/mtd/maps/Config.in	2006-12-11 18:18:37.000000000 +0100
@@ -1,6 +1,6 @@
 # drivers/mtd/maps/Config.in
 
-# $Id: Config.in,v 1.67 2004/04/12 06:38:39 ppopov Exp $
+# $Id: Config.in,v 1.2 2005/09/28 14:19:12 lel Exp $
 
 mainmenu_option next_comment
 
@@ -9,6 +9,11 @@
 bool '  Support for non-linear mappings of flash chips' CONFIG_MTD_COMPLEX_MAPPINGS
 
 bool '  CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE
+if [ "$CONFIG_MTD_PHYSMAP" = "y" ]; then
+   hex '    Physical start address of flash mapping' CONFIG_MTD_PHYSMAP_START 0x8000000
+   hex '    Physical length of flash mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000
+   int '    Bank width in octets' CONFIG_MTD_PHYSMAP_BANKWIDTH 2
+fi
 
 if [ "$CONFIG_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
    dep_tristate '  Sun Microsystems userflash support' CONFIG_MTD_SUN_UFLASH $CONFIG_MTD_CFI
@@ -29,7 +34,7 @@
    dep_tristate '  Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI
    dep_tristate '  BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDECPROBE
    dep_tristate '  ROM connected to AMD76X southbridge' CONFIG_MTD_AMD76XROM $CONFIG_MTD_GEN_PROBE   
-   dep_tristate '  ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE $CONFIG_MTD_COMPLEX_MAPPINGS
+   dep_tristate '  ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE
    dep_tristate '  CFI Flash device mapped on SnapGear/SecureEdge' CONFIG_MTD_NETtel $CONFIG_MTD_PARTITIONS
    dep_tristate '  BIOS flash chip on Intel SCB2 boards' CONFIG_MTD_SCB2_FLASH $CONFIG_MTD_GEN_PROBE
 fi
@@ -60,32 +65,20 @@
       fi
       dep_tristate '  CFI Flash device mapped on IBM Beech' CONFIG_MTD_BEECH $CONFIG_MTD_CFI $CONFIG_BEECH
       dep_tristate '  CFI Flash device mapped on IBM Arctic' CONFIG_MTD_ARCTIC $CONFIG_MTD_CFI $CONFIG_ARCTIC2
+      dep_tristate '  Flash device mapped on IBM Walnut' CONFIG_MTD_WALNUT $CONFIG_MTD_JEDECPROBE $CONFIG_WALNUT
     fi
     if [ "$CONFIG_440" = "y" ]; then
-      dep_tristate '  Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_CFI $CONFIG_EBONY
+      dep_tristate '  Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_JEDECPROBE $CONFIG_EBONY
     fi
   fi
 fi
 
 if [ "$CONFIG_MIPS" = "y" -o "$CONFIG_MIPS64" = "y" ]; then
-
-	if [ "$CONFIG_MIPS_PB1000" = "y" -o \
-		 "$CONFIG_MIPS_PB1500" = "y" -o \
-		 "$CONFIG_MIPS_PB1100" = "y" -o \
-		 "$CONFIG_MIPS_PB1550" = "y" -o \
-		 "$CONFIG_MIPS_PB1200" = "y" -o \
-		 "$CONFIG_MIPS_DB1000" = "y" -o \
-		 "$CONFIG_MIPS_DB1500" = "y" -o \
-		 "$CONFIG_MIPS_DB1100" = "y" -o \
-		 "$CONFIG_MIPS_DB1550" = "y" -o \
-		 "$CONFIG_MIPS_DB1200" = "y" -o \
-		 "$CONFIG_MIPS_HYDROGEN3" = "y" -o \
-		 "$CONFIG_MIPS_BOSPORUS" = "y" -o \
-		 "$CONFIG_MIPS_MIRAGE" = "y" ]; then
-	tristate '  AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support' CONFIG_MTD_ALCHEMY
+   if [ "$CONFIG_MIPS_I3_MMB3" = "y" ]; then
+      bool '  Mood 300 MTD support' CONFIG_MTD_MOOD300
    fi
 
-   dep_tristate '  XXS1500 boot flash device' CONFIG_MTD_XXS1500 $CONFIG_MIPS_XXS1500
+   dep_tristate '  AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support' CONFIG_MTD_ALCHEMY $CONFIG_SOC_AU1X00
    dep_tristate '  Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS 
    if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then
       hex '    Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/maps/Makefile linux-2.4.30/drivers/mtd/maps/Makefile
--- linux-2.4.30-orig/drivers/mtd/maps/Makefile	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/drivers/mtd/maps/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -1,69 +1,11 @@
 #
-# linux/drivers/maps/Makefile
+# linux/drivers/maps/Makefile.24
+# Makefile for obsolete kernels
 #
-# $Id: Makefile.common,v 1.10 2004/04/12 06:40:41 ppopov Exp $
+# $Id: Makefile,v 1.2 2005/09/28 14:19:12 lel Exp $
 
-ifeq ($(PATCHLEVEL),4)
 O_TARGET	:= mapslink.o
 export-objs	:= map_funcs.o
-endif
 
-
-ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
-obj-$(CONFIG_MTD)		+= map_funcs.o
-endif
-
-# Chip mappings
-obj-$(CONFIG_MTD_CDB89712)	+= cdb89712.o
-obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o
-obj-$(CONFIG_MTD_CFI_FLAGADM)	+= cfi_flagadm.o
-obj-$(CONFIG_MTD_CSTM_MIPS_IXX)	+= cstm_mips_ixx.o
-obj-$(CONFIG_MTD_DC21285)	+= dc21285.o
-obj-$(CONFIG_MTD_DILNETPC)	+= dilnetpc.o
-obj-$(CONFIG_MTD_ELAN_104NC)	+= elan-104nc.o
-obj-$(CONFIG_MTD_EPXA10DB)	+= epxa10db-flash.o
-obj-$(CONFIG_MTD_IQ80310)	+= iq80310.o
-obj-$(CONFIG_MTD_L440GX)	+= l440gx.o
-obj-$(CONFIG_MTD_AMD76XROM)	+= amd76xrom.o
-obj-$(CONFIG_MTD_ICHXROM)	+= ichxrom.o
-obj-$(CONFIG_MTD_TSUNAMI)	+= tsunami_flash.o
-obj-$(CONFIG_MTD_LUBBOCK)	+= lubbock-flash.o
-obj-$(CONFIG_MTD_MBX860)	+= mbx860.o
-obj-$(CONFIG_MTD_CEIVA)		+= ceiva.o
-obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
-obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o 
-obj-$(CONFIG_MTD_PNC2000)	+= pnc2000.o
-obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
-obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o
-obj-$(CONFIG_MTD_TQM8XXL)	+= tqm8xxl.o
-obj-$(CONFIG_MTD_SA1100)	+= sa1100-flash.o
-obj-$(CONFIG_MTD_SBC_GXX)	+= sbc_gxx.o
-obj-$(CONFIG_MTD_SC520CDP)	+= sc520cdp.o
-obj-$(CONFIG_MTD_NETSC520)	+= netsc520.o
-obj-$(CONFIG_MTD_SUN_UFLASH)	+= sun_uflash.o
-obj-$(CONFIG_MTD_VMAX)		+= vmax301.o
-obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
-obj-$(CONFIG_MTD_DBOX2)		+= dbox2-flash.o
-obj-$(CONFIG_MTD_OCELOT)	+= ocelot.o
-obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
-obj-$(CONFIG_MTD_PCI)		+= pci.o
-obj-$(CONFIG_MTD_ALCHEMY)	+= alchemy-flash.o
-obj-$(CONFIG_MTD_LASAT)		+= lasat.o
-obj-$(CONFIG_MTD_AUTCPU12)	+= autcpu12-nvram.o
-obj-$(CONFIG_MTD_EDB7312)	+= edb7312.o
-obj-$(CONFIG_MTD_IMPA7)		+= impa7.o
-obj-$(CONFIG_MTD_FORTUNET)	+= fortunet.o
-obj-$(CONFIG_MTD_REDWOOD)	+= redwood.o
-obj-$(CONFIG_MTD_UCLINUX)	+= uclinux.o
-obj-$(CONFIG_MTD_NETtel)	+= nettel.o
-obj-$(CONFIG_MTD_SCB2_FLASH)	+= scb2_flash.o
-obj-$(CONFIG_MTD_EBONY)		+= ebony.o
-obj-$(CONFIG_MTD_BEECH)		+= beech-mtd.o
-obj-$(CONFIG_MTD_ARCTIC)	+= arctic-mtd.o
-obj-$(CONFIG_MTD_H720X)		+= h720x-flash.o
-obj-$(CONFIG_MTD_SBC8240)	+= sbc8240.o
-obj-$(CONFIG_MTD_NOR_TOTO)	+= omap-toto-flash.o
-obj-$(CONFIG_MTD_MPC1211)	+= mpc1211.o
-obj-$(CONFIG_MTD_IXP425)	+= ixp425.o
-
--include $(TOPDIR)/Rules.make
+include Makefile.common
+include $(TOPDIR)/Rules.make
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/maps/Makefile.common linux-2.4.30/drivers/mtd/maps/Makefile.common
--- linux-2.4.30-orig/drivers/mtd/maps/Makefile.common	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/maps/Makefile.common	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,73 @@
+#
+# linux/drivers/maps/Makefile
+#
+# $Id: Makefile.common,v 1.2 2005/09/28 14:19:12 lel Exp $
+
+ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
+obj-$(CONFIG_MTD)		+= map_funcs.o
+endif
+
+# Chip mappings
+obj-$(CONFIG_MTD_CDB89712)	+= cdb89712.o
+obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o
+obj-$(CONFIG_MTD_BAST)		+= bast-flash.o
+obj-$(CONFIG_MTD_CFI_FLAGADM)	+= cfi_flagadm.o
+obj-$(CONFIG_MTD_CSTM_MIPS_IXX)	+= cstm_mips_ixx.o
+obj-$(CONFIG_MTD_DC21285)	+= dc21285.o
+obj-$(CONFIG_MTD_DILNETPC)	+= dilnetpc.o
+obj-$(CONFIG_MTD_EPXA10DB)	+= epxa10db-flash.o
+obj-$(CONFIG_MTD_IQ80310)	+= iq80310.o
+obj-$(CONFIG_MTD_L440GX)	+= l440gx.o
+obj-$(CONFIG_MTD_AMD76XROM)	+= amd76xrom.o
+obj-$(CONFIG_MTD_ICHXROM)	+= ichxrom.o
+obj-$(CONFIG_MTD_TSUNAMI)	+= tsunami_flash.o
+obj-$(CONFIG_MTD_LUBBOCK)	+= lubbock-flash.o
+obj-$(CONFIG_MTD_MBX860)	+= mbx860.o
+obj-$(CONFIG_MTD_CEIVA)		+= ceiva.o
+obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
+obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o 
+obj-$(CONFIG_MTD_MULTI_PHYSMAP)	+= mphysmap.o
+obj-$(CONFIG_MTD_PNC2000)	+= pnc2000.o
+obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
+obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o
+obj-$(CONFIG_MTD_TQM8XXL)	+= tqm8xxl.o
+obj-$(CONFIG_MTD_SA1100)	+= sa1100-flash.o
+obj-$(CONFIG_MTD_IPAQ)		+= ipaq-flash.o
+obj-$(CONFIG_MTD_SBC_GXX)	+= sbc_gxx.o
+obj-$(CONFIG_MTD_SC520CDP)	+= sc520cdp.o
+obj-$(CONFIG_MTD_NETSC520)	+= netsc520.o
+obj-$(CONFIG_MTD_TS5500)	+= ts5500_flash.o
+obj-$(CONFIG_MTD_SUN_UFLASH)	+= sun_uflash.o
+obj-$(CONFIG_MTD_VMAX)		+= vmax301.o
+obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
+obj-$(CONFIG_MTD_DBOX2)		+= dbox2-flash.o
+obj-$(CONFIG_MTD_OCELOT)	+= ocelot.o
+obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
+obj-$(CONFIG_MTD_PCI)		+= pci.o
+obj-$(CONFIG_MTD_ALCHEMY)       += alchemy-flash.o
+obj-$(CONFIG_MTD_MOOD300)       += mood300-flash.o
+obj-$(CONFIG_MTD_LASAT)		+= lasat.o
+obj-$(CONFIG_MTD_AUTCPU12)	+= autcpu12-nvram.o
+obj-$(CONFIG_MTD_EDB7312)	+= edb7312.o
+obj-$(CONFIG_MTD_IMPA7)		+= impa7.o
+obj-$(CONFIG_MTD_FORTUNET)	+= fortunet.o
+obj-$(CONFIG_MTD_REDWOOD)	+= redwood.o
+obj-$(CONFIG_MTD_CHESTNUT)	+= chestnut.o
+obj-$(CONFIG_MTD_UCLINUX)	+= uclinux.o
+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
+obj-$(CONFIG_MTD_NOR_TOTO)	+= omap-toto-flash.o
+obj-$(CONFIG_MTD_MPC1211)	+= mpc1211.o
+obj-$(CONFIG_MTD_IXP4XX)	+= ixp4xx.o
+obj-$(CONFIG_MTD_IXP2000)	+= ixp2000.o
+obj-$(CONFIG_MTD_WRSBC8260)	+= wr_sbc82xx_flash.o
+obj-$(CONFIG_MTD_DMV182)	+= dmv182.o
+obj-$(CONFIG_MTD_SHARP_SL)	+= sharpsl-flash.o
+obj-$(CONFIG_MTD_PLATRAM)	+= plat-ram.o
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/maps/alchemy-flash.c linux-2.4.30/drivers/mtd/maps/alchemy-flash.c
--- linux-2.4.30-orig/drivers/mtd/maps/alchemy-flash.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/drivers/mtd/maps/alchemy-flash.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,9 +1,8 @@
 /*
  * Flash memory access on AMD Alchemy evaluation boards
  * 
- * $Id: db1550-flash.c,v 1.7 2004/11/04 13:24:14 gleixner Exp $
+ * $Id: alchemy-flash.c,v 1.2 2005/09/28 14:19:12 lel Exp $
  *
- * (C) 2004 Embedded Edge, LLC, based on db1550-flash.c:
  * (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com>
  * 
  */
@@ -74,11 +73,17 @@
 #define BOARD_FLASH_WIDTH 4 /* 32-bits */
 #endif
 
+#ifdef CONFIG_MIPS_I3_MMB3 
+#define BOARD_MAP_NAME "Db1550 Flash"
+#define BOARD_FLASH_SIZE 0x00200000 /* 2MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#else
 #ifdef CONFIG_MIPS_DB1550
 #define BOARD_MAP_NAME "Db1550 Flash"
 #define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
 #define BOARD_FLASH_WIDTH 4 /* 32-bits */
 #endif
+#endif
 
 #ifdef CONFIG_MIPS_DB1200
 #define BOARD_MAP_NAME "Db1200 Flash"
@@ -106,78 +111,8 @@
 #define USE_LOCAL_ACCESSORS /* why? */
 #endif
 
-
-#ifdef USE_LOCAL_ACCESSORS
-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
-{
-	__u8 ret;
-	ret = __raw_readb(map->map_priv_1 + ofs);
-	DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
-	return ret;
-}
-
-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
-{
-	__u16 ret;
-	ret = __raw_readw(map->map_priv_1 + ofs);
-	DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
-	return ret;
-}
-
-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
-{
-	__u32 ret;
-	ret = __raw_readl(map->map_priv_1 + ofs);
-	DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
-	return ret;
-}
-
-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to);
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from);
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
-#endif
-
 static struct map_info alchemy_map = {
 	.name =	BOARD_MAP_NAME,
-#ifdef USE_LOCAL_ACCESSORS
-	read8: physmap_read8,
-	read16: physmap_read16,
-	read32: physmap_read32,
-	copy_from: physmap_copy_from,
-	write8: physmap_write8,
-	write16: physmap_write16,
-	write32: physmap_write32,
-	copy_to: physmap_copy_to,
-#endif
 };
 
 static struct mtd_partition alchemy_partitions[] = {
@@ -188,12 +123,12 @@
         },{
                 .name = "YAMON",
                 .size = 0x0100000,
-				.offset = MTDPART_OFS_APPEND,
+		.offset = MTDPART_OFS_APPEND,
                 .mask_flags = MTD_WRITEABLE
         },{
                 .name = "raw kernel",
-				.size = (0x300000 - 0x40000), /* last 256KB is yamon env */
-				.offset = MTDPART_OFS_APPEND,
+		.size = (0x300000 - 0x40000), /* last 256KB is yamon env */
+		.offset = MTDPART_OFS_APPEND,
         }
 };
 
@@ -209,7 +144,7 @@
 	unsigned long window_size;
 	
 	/* Default flash buswidth */
-	alchemy_map.buswidth = BOARD_FLASH_WIDTH;
+	alchemy_map.bankwidth = BOARD_FLASH_WIDTH;
 
 	window_addr = 0x20000000 - BOARD_FLASH_SIZE;
 	window_size = BOARD_FLASH_SIZE;
@@ -233,10 +168,13 @@
 	 * specific machine settings might have been set above.
 	 */
 	printk(KERN_NOTICE BOARD_MAP_NAME ": probing %d-bit flash bus\n", 
-			alchemy_map.buswidth*8);
+			alchemy_map.bankwidth*8);
 	alchemy_map.virt = ioremap(window_addr, window_size);
 	mymtd = do_map_probe("cfi_probe", &alchemy_map);
-	if (!mymtd) return -ENXIO;
+	if (!mymtd) {
+		iounmap(alchemy_map.virt);
+		return -ENXIO;
+	}
 	mymtd->owner = THIS_MODULE;
 
 	add_mtd_partitions(mymtd, parts, nb_parts);
@@ -248,14 +186,13 @@
 	if (mymtd) {
 		del_mtd_partitions(mymtd);
 		map_destroy(mymtd);
-		iounmap((void *) alchemy_map.virt);
+		iounmap(alchemy_map.virt);
 	}
 }
 
 module_init(alchemy_mtd_init);
 module_exit(alchemy_mtd_cleanup);
 
-MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_AUTHOR("Embedded Alley Solutions, Inc");
 MODULE_DESCRIPTION(BOARD_MAP_NAME " MTD driver");
 MODULE_LICENSE("GPL");
-
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/maps/mood300-flash.c linux-2.4.30/drivers/mtd/maps/mood300-flash.c
--- linux-2.4.30-orig/drivers/mtd/maps/mood300-flash.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/maps/mood300-flash.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,163 @@
+/*
+ * Flash memory access on Mood 300 board
+ * 
+ * (C) 2004 I3 Micro Technology AB based on db1550-flash.c:
+ * (C) 2004 Embedded Edge, LLC, based on db1550-flash.c:
+ * (C) 2003 Pete Popov <pete_popov@yahoo.com>
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/au1000.h>
+
+#ifdef 	DEBUG_RW
+#define	DBG(x...)	printk(x)
+#else
+#define	DBG(x...)	
+#endif
+
+static unsigned long window_addr;
+static unsigned long window_size;
+
+
+static struct map_info mood300_map = {
+	.name =	"Mood 300 flash",
+};
+
+static unsigned char flash_buswidth = 2;
+
+/* 
+ * Support only 16/32MB NOR Flash parts
+ */
+
+static struct mtd_partition mood300_partitions[] = {
+        {
+                .name = "all",
+                .size =   0x00050000,
+                .offset = 0x0000000,
+		.mask_flags = MTD_WRITEABLE,
+	},{
+                .name = "mtd1",
+                .size =   0x00030000,
+                .offset = 0x0000000,
+		.mask_flags = MTD_WRITEABLE,
+        },{
+                .name = "mtd2",
+		.size = 0x00010000,
+		.offset = 0x00030000,
+        },{
+                .name = "mtd3",
+		.size = 0x00010000,
+		.offset = 0x00040000,
+		.mask_flags = MTD_WRITEABLE,
+        },{
+                .name = "mtd4",
+                .size = 0x00120000,
+		.offset = 0x000E0000,
+        },{
+                .name = "mtd7",
+                .size = 0x00030000,
+		.offset = 0x00050000,
+		.mask_flags = MTD_WRITEABLE,
+        },{
+                .name = "mtd8",
+                .size = 0x00030000,
+		.offset = 0x00080000,
+		.mask_flags = MTD_WRITEABLE,
+        }
+	,{
+                .name = "mtd9",
+                .size = 0x00030000,
+		.offset = 0x000b0000,
+		.mask_flags = MTD_WRITEABLE,
+        }
+
+};
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_info *mymtd;
+
+/*
+ * Probe the flash density and setup window address and size
+ * based on user CONFIG options. There are times when we don't
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+int setup_flash_params(void)
+{
+    window_addr = 0x1FC00000;
+
+#ifdef CONFIG_MTD_MOOD300_2MB
+    window_size = 0x00200000; 
+#else
+    window_size = 0x00400000; 
+#endif
+
+    return 0;
+}
+
+int __init mood300_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	
+	/* Default flash buswidth */
+	mood300_map.bankwidth = flash_buswidth;
+
+	if (setup_flash_params()) 
+		return -ENXIO;
+
+	/*
+	 * Static partition definition selection
+	 */
+	parts = mood300_partitions;
+	nb_parts = NB_OF(mood300_partitions);
+	mood300_map.size = window_size;
+
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "Mood 300 flash: probing %d-bit flash bus\n", 
+			mood300_map.bankwidth*8);
+	mood300_map.virt = 
+		(void __iomem *)ioremap(window_addr, window_size);
+	printk("ioremap(0x%08x, 0x%08x) = 0x%08x\n", (unsigned int)window_addr, (unsigned int)window_size, (unsigned int)mood300_map.virt);
+	mymtd = do_map_probe("cfi_probe", &mood300_map);
+	if (!mymtd) return -ENXIO;
+	mymtd->owner = THIS_MODULE;
+
+	OccupyMtdNumber(5);
+	OccupyMtdNumber(6);
+	add_mtd_partitions(mymtd, parts, nb_parts);
+	ReleaseMtdNumber(5);
+	ReleaseMtdNumber(6);
+	
+	return 0;
+}
+
+static void __exit mood300_mtd_cleanup(void)
+{
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+	}
+}
+
+module_init(mood300_mtd_init);
+module_exit(mood300_mtd_cleanup);
+
+MODULE_AUTHOR("I3 Micro Technology AB");
+MODULE_DESCRIPTION("Mood 300 mtd map driver");
+MODULE_LICENSE("GPL");
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/maps/pb1550-flash.c linux-2.4.30/drivers/mtd/maps/pb1550-flash.c
--- linux-2.4.30-orig/drivers/mtd/maps/pb1550-flash.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/maps/pb1550-flash.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,202 @@
+/*
+ * Flash memory access on Alchemy Pb1550 board
+ * 
+ * (C) 2004 Embedded Edge, LLC, based on pb1550-flash.c:
+ * (C) 2003 Pete Popov <ppopov@pacbell.net>
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/au1000.h>
+#include <asm/pb1550.h>
+
+#ifdef 	DEBUG_RW
+#define	DBG(x...)	printk(x)
+#else
+#define	DBG(x...)	
+#endif
+
+static unsigned long window_addr;
+static unsigned long window_size;
+
+
+static struct map_info pb1550_map = {
+	.name =	"Pb1550 flash",
+};
+
+static unsigned char flash_buswidth = 4;
+
+/* 
+ * Support only 64MB NOR Flash parts
+ */
+
+#ifdef PB1550_BOTH_BANKS
+/* both banks will be used. Combine the first bank and the first 
+ * part of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+static struct mtd_partition pb1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+	 * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size =   (0x1FC00000 - 0x18000000),
+                .offset = 0x0000000
+        },{
+                .name = "yamon",
+                .size = 0x0100000,
+		.offset = MTDPART_OFS_APPEND,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "raw kernel",
+		.size = (0x300000 - 0x40000), /* last 256KB is yamon env */
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#elif defined(PB1550_BOOT_ONLY)
+static struct mtd_partition pb1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size =   0x03c00000,
+                .offset = 0x0000000
+        },{
+                .name = "yamon",
+                .size = 0x0100000,
+		.offset = MTDPART_OFS_APPEND,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "raw kernel",
+		.size = (0x300000-0x40000), /* last 256KB is yamon env */
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#elif defined(PB1550_USER_ONLY)
+static struct mtd_partition pb1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size = (0x4000000 - 0x200000), /* reserve 2MB for raw kernel */
+                .offset = 0x0000000
+        },{
+                .name = "raw kernel",
+		.size = MTDPART_SIZ_FULL,
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#else
+#error MTD_PB1550 define combo error /* should never happen */
+#endif
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_info *mymtd;
+
+/*
+ * Probe the flash density and setup window address and size
+ * based on user CONFIG options. There are times when we don't
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+int setup_flash_params(void)
+{
+	u16 boot_swapboot;
+	boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) | 
+		((bcsr->status >> 6)  & 0x1);
+	printk("Pb1550 MTD: boot:swap %d\n", boot_swapboot);
+
+	switch (boot_swapboot) {
+		case 0: /* 512Mbit devices, both enabled */
+		case 1: 
+		case 8:
+		case 9: 
+#if defined(PB1550_BOTH_BANKS)
+			window_addr = 0x18000000;
+			window_size = 0x8000000; 
+#elif defined(PB1550_BOOT_ONLY)
+			window_addr = 0x1C000000;
+			window_size = 0x4000000; 
+#else /* USER ONLY */
+			window_addr = 0x1E000000;
+			window_size = 0x4000000; 
+#endif
+			break;
+		case 0xC:
+		case 0xD:
+		case 0xE:
+		case 0xF: 
+			/* 64 MB Boot NOR Flash is disabled */
+			/* and the start address is moved to 0x0C00000 */
+			window_addr = 0x0C000000;
+			window_size = 0x4000000; 
+		default:
+			printk("Pb1550 MTD: unsupported boot:swap setting\n");
+			return 1;
+	}
+	return 0;
+}
+
+int __init pb1550_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	
+	/* Default flash buswidth */
+	pb1550_map.buswidth = flash_buswidth;
+
+	if (setup_flash_params()) 
+		return -ENXIO;
+
+	/*
+	 * Static partition definition selection
+	 */
+	parts = pb1550_partitions;
+	nb_parts = NB_OF(pb1550_partitions);
+	pb1550_map.size = window_size;
+
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "Pb1550 flash: probing %d-bit flash bus\n", 
+			pb1550_map.buswidth*8);
+	pb1550_map.virt = 
+		(unsigned long)ioremap(window_addr, window_size);
+	mymtd = do_map_probe("cfi_probe", &pb1550_map);
+	if (!mymtd) return -ENXIO;
+	mymtd->owner = THIS_MODULE;
+
+	add_mtd_partitions(mymtd, parts, nb_parts);
+	return 0;
+}
+
+static void __exit pb1550_mtd_cleanup(void)
+{
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+	}
+}
+
+module_init(pb1550_mtd_init);
+module_exit(pb1550_mtd_cleanup);
+
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("Pb1550 mtd map driver");
+MODULE_LICENSE("GPL");
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/maps/pb1xxx-flash.c linux-2.4.30/drivers/mtd/maps/pb1xxx-flash.c
--- linux-2.4.30-orig/drivers/mtd/maps/pb1xxx-flash.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/maps/pb1xxx-flash.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,179 @@
+/*
+ * Flash memory access on Alchemy Pb1xxx boards
+ * 
+ * (C) 2001 Pete Popov <ppopov@mvista.com>
+ * 
+ * $Id: pb1xxx-flash.c,v 1.2 2005/09/28 14:19:26 lel Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/au1000.h>
+
+#ifdef 	DEBUG_RW
+#define	DBG(x...)	printk(x)
+#else
+#define	DBG(x...)	
+#endif
+
+#ifdef CONFIG_MIPS_PB1000
+
+#define WINDOW_ADDR 0x1F800000
+#define WINDOW_SIZE 0x800000
+
+static struct mtd_partition pb1xxx_partitions[] = {
+        {
+                .name         =  "yamon env",
+                .size         =   0x00020000,
+                .offset       =   0,
+                .mask_flags   =   MTD_WRITEABLE},
+	{
+                .name         =   "User FS",
+                .size         =   0x003e0000,
+                .offset       =   0x20000,},
+	{
+                .name         =   "boot code",
+                .size         =   0x100000,
+                .offset       =   0x400000,
+                .mask_flags   =   MTD_WRITEABLE},
+	{
+                .name         =   "raw/kernel",
+                .size         =   0x300000,
+                .offset       =   0x500000}
+};
+
+#elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
+
+#if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
+/* both 32MB banks will be used. Combine the first 32MB bank and the
+ * first 28MB of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+#define WINDOW_ADDR 0x1C000000
+#define WINDOW_SIZE 0x4000000
+static struct mtd_partition pb1xxx_partitions[] = {
+        {
+                .name         =   "User FS",
+                .size         =   0x3c00000,
+                .offset       =   0x0000000
+        },{
+                .name         =   "yamon",
+                .size         =   0x0100000,
+                .offset       =   0x3c00000,
+                .mask_flags   =   MTD_WRITEABLE
+        },{
+                .name         =   "raw kernel",
+                .size         =   0x02c0000,
+                .offset       =   0x3d00000
+        }
+};
+#elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER)
+#define WINDOW_ADDR 0x1E000000
+#define WINDOW_SIZE 0x2000000
+static struct mtd_partition pb1xxx_partitions[] = {
+        {
+                .name         =   "User FS",
+                .size         =   0x1c00000,
+                .offset       =   0x0000000
+        },{
+                .name         =   "yamon",
+                .size         =   0x0100000,
+                .offset       =   0x1c00000,
+                .mask_flags   =   MTD_WRITEABLE
+        },{
+                .name         =   "raw kernel",
+                .size         =   0x02c0000,
+                .offset       =   0x1d00000
+        }
+};
+#elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
+#define WINDOW_ADDR 0x1C000000
+#define WINDOW_SIZE 0x2000000
+static struct mtd_partition pb1xxx_partitions[] = {
+        {
+                .name         =   "User FS",
+                .size         =    0x1e00000,
+                .offset       =    0x0000000
+        },{
+                .name         =    "raw kernel",
+                .size         =    0x0200000,
+                .offset       =    0x1e00000,
+        }
+};
+#else
+#error MTD_PB1500 define combo error /* should never happen */
+#endif
+#else
+#error Unsupported board
+#endif
+
+#define NAME     	"Pb1x00 Linux Flash"
+#define PADDR    	WINDOW_ADDR
+#define BUSWIDTH	4
+#define SIZE		WINDOW_SIZE
+#define PARTITIONS	4
+
+static struct map_info pb1xxx_mtd_map = {
+	.name		= NAME,
+	.size		= SIZE,
+	.buswidth	= BUSWIDTH,
+	.phys		= PADDR,
+};
+
+static struct mtd_info *pb1xxx_mtd;
+
+int __init pb1xxx_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	char *part_type;
+	
+	/*
+	 * Static partition definition selection
+	 */
+	part_type = "static";
+	parts = pb1xxx_partitions;
+	nb_parts = ARRAY_SIZE(pb1xxx_partitions);
+
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n", 
+			BUSWIDTH*8);
+	pb1xxx_mtd_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+	simple_map_init(&pb1xxx_mtd_map);
+
+	pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map);
+	if (!pb1xxx_mtd) return -ENXIO;
+	pb1xxx_mtd->owner = THIS_MODULE;
+
+	add_mtd_partitions(pb1xxx_mtd, parts, nb_parts);
+	return 0;
+}
+
+static void __exit pb1xxx_mtd_cleanup(void)
+{
+	if (pb1xxx_mtd) {
+		del_mtd_partitions(pb1xxx_mtd);
+		map_destroy(pb1xxx_mtd);
+		iounmap((void *) pb1xxx_mtd_map.virt);
+	}
+}
+
+module_init(pb1xxx_mtd_init);
+module_exit(pb1xxx_mtd_cleanup);
+
+MODULE_AUTHOR("Pete Popov");
+MODULE_DESCRIPTION("Pb1xxx CFI map driver");
+MODULE_LICENSE("GPL");
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/maps/physmap.c linux-2.4.30/drivers/mtd/maps/physmap.c
--- linux-2.4.30-orig/drivers/mtd/maps/physmap.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/maps/physmap.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: physmap.c,v 1.30 2003/11/11 19:05:18 jsun Exp $
+ * $Id: physmap.c,v 1.2 2005/09/28 14:19:26 lel Exp $
  *
  * Normal mappings of chips in physical memory
  *
@@ -22,7 +22,12 @@
 
 static struct mtd_info *mymtd;
 
-struct map_info physmap_map = {.name = "phys_mapped_flash"};
+struct map_info physmap_map = {
+	.name = "phys_mapped_flash",
+	.phys = CONFIG_MTD_PHYSMAP_START,
+	.size = CONFIG_MTD_PHYSMAP_LEN,
+	.bankwidth = CONFIG_MTD_PHYSMAP_BANKWIDTH,
+};
 
 #ifdef CONFIG_MTD_PARTITIONS
 static struct mtd_partition *mtd_parts;
@@ -31,7 +36,7 @@
 static int num_physmap_partitions;
 static struct mtd_partition *physmap_partitions;
 
-char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
+static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
 
 void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
 {
@@ -40,13 +45,13 @@
 }
 #endif /* CONFIG_MTD_PARTITIONS */
 
-int __init init_physmap(void)
+static int __init init_physmap(void)
 {
-	static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 };
+	static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
 	const char **type;
 
        	printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys);
-	physmap_map.virt = (unsigned long)ioremap(physmap_map.phys, physmap_map.size);
+	physmap_map.virt = ioremap(physmap_map.phys, physmap_map.size);
 
 	if (!physmap_map.virt) {
 		printk("Failed to ioremap\n");
@@ -55,7 +60,7 @@
 
 	simple_map_init(&physmap_map);
 
-	mymtd = 0;
+	mymtd = NULL;
 	type = rom_probe_types;
 	for(; !mymtd && *type; type++) {
 		mymtd = do_map_probe(*type, &physmap_map);
@@ -87,7 +92,7 @@
 		return 0;
 	}
 
-	iounmap((void *)physmap_map.virt);
+	iounmap(physmap_map.virt);
 	return -ENXIO;
 }
 
@@ -107,8 +112,8 @@
 #endif
 	map_destroy(mymtd);
 
-	iounmap((void *)physmap_map.virt);
-	physmap_map.virt = 0;
+	iounmap(physmap_map.virt);
+	physmap_map.virt = NULL;
 }
 
 module_init(init_physmap);
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/mtd_blkdevs-24.c linux-2.4.30/drivers/mtd/mtd_blkdevs-24.c
--- linux-2.4.30-orig/drivers/mtd/mtd_blkdevs-24.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/mtd_blkdevs-24.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: mtd_blkdevs-24.c,v 1.15 2003/10/10 08:55:03 dwmw2 Exp $
+ * $Id: mtd_blkdevs-24.c,v 1.2 2005/09/28 14:18:56 lel Exp $
  *
  * (C) 2003 David Woodhouse <dwmw2@infradead.org>
  *
@@ -124,19 +124,12 @@
 	   actually want to deal with signals. We can't just call 
 	   exit_sighand() since that'll cause an oops when we finally
 	   do exit. */
-
-#ifndef __rh_config_h__ /* HAVE_NPTL */
 	spin_lock_irq(&current->sigmask_lock);
 	sigfillset(&current->blocked);
-	recalc_sigpending(current);
-	spin_unlock_irq(&current->sigmask_lock);
-#else
-	spin_lock_irq(&current->sighand->siglock);
-	sigfillset(&current->blocked);
 	recalc_sigpending();
-	spin_unlock_irq(&current->sighand->siglock);
-#endif
-	daemonize();
+	spin_unlock_irq(&current->sigmask_lock);
+
+	daemonize("%sd", tr->name);
 
 	while (!tr->blkcore_priv->exiting) {
 		struct request *req;
@@ -322,7 +315,7 @@
 	}
 
 	grok_partitions(gd, minor, 1 << tr->part_bits, 
-			tr->blkcore_priv->sizes[minor]);
+                        dev->size);
 	up(&mtd_table_mutex);
 
 	return 0;
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/mtd_blkdevs.c linux-2.4.30/drivers/mtd/mtd_blkdevs.c
--- linux-2.4.30-orig/drivers/mtd/mtd_blkdevs.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/mtd_blkdevs.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: mtd_blkdevs.c,v 1.21 2004/03/30 20:59:46 sjhill Exp $
+ * $Id: mtd_blkdevs.c,v 1.2 2005/09/28 14:18:56 lel Exp $
  *
  * (C) 2003 David Woodhouse <dwmw2@infradead.org>
  *
@@ -81,7 +81,7 @@
 	struct request_queue *rq = tr->blkcore_priv->rq;
 
 	/* we might get involved when memory gets low, so use PF_MEMALLOC */
-	current->flags |= PF_MEMALLOC;
+	current->flags |= PF_MEMALLOC | PF_NOFREEZE;
 
 	daemonize("%sd", tr->name);
 
@@ -143,7 +143,7 @@
 }
 
 
-int blktrans_open(struct inode *i, struct file *f)
+static int blktrans_open(struct inode *i, struct file *f)
 {
 	struct mtd_blktrans_dev *dev;
 	struct mtd_blktrans_ops *tr;
@@ -174,7 +174,7 @@
 	return ret;
 }
 
-int blktrans_release(struct inode *i, struct file *f)
+static int blktrans_release(struct inode *i, struct file *f)
 {
 	struct mtd_blktrans_dev *dev;
 	struct mtd_blktrans_ops *tr;
@@ -216,12 +216,11 @@
 
 			memset(&g, 0, sizeof(g));
 			ret = tr->getgeo(dev, &g);
-
 			if (ret)
 				return ret;
 
 			g.start = get_start_sect(inode->i_bdev);
-			if (copy_to_user((void *)arg, &g, sizeof(g)))
+			if (copy_to_user((void __user *)arg, &g, sizeof(g)))
 				return -EFAULT;
 			return 0;
 		} /* else */
@@ -327,7 +326,7 @@
 	return 0;
 }
 
-void blktrans_notify_remove(struct mtd_info *mtd)
+static void blktrans_notify_remove(struct mtd_info *mtd)
 {
 	struct list_head *this, *this2, *next;
 
@@ -343,7 +342,7 @@
 	}
 }
 
-void blktrans_notify_add(struct mtd_info *mtd)
+static void blktrans_notify_add(struct mtd_info *mtd)
 {
 	struct list_head *this;
 
@@ -403,8 +402,7 @@
 
 	tr->blkcore_priv->rq->queuedata = tr;
 
-	ret = kernel_thread(mtd_blktrans_thread, tr, 
-			    CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
+	ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL);
 	if (ret < 0) {
 		blk_cleanup_queue(tr->blkcore_priv->rq);
 		unregister_blkdev(tr->major, tr->name);
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/mtdblock.c linux-2.4.30/drivers/mtd/mtdblock.c
--- linux-2.4.30-orig/drivers/mtd/mtdblock.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/mtdblock.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,7 +1,7 @@
 /* 
  * Direct MTD block device access
  *
- * $Id: mtdblock.c,v 1.64 2003/10/04 17:14:14 dwmw2 Exp $
+ * $Id: mtdblock.c,v 1.2 2005/09/28 14:18:56 lel Exp $
  *
  * (C) 2000-2003 Nicolas Pitre <nico@cam.org>
  * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
@@ -248,7 +248,7 @@
 			      unsigned long block, char *buf)
 {
 	struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
-	if (unlikely(!mtdblk->cache_data)) {
+	if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {
 		mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
 		if (!mtdblk->cache_data)
 			return -EINTR;
@@ -361,7 +361,7 @@
 	kfree(dev);
 }
 
-struct mtd_blktrans_ops mtdblock_tr = {
+static struct mtd_blktrans_ops mtdblock_tr = {
 	.name		= "mtdblock",
 	.major		= 31,
 	.part_bits	= 0,
@@ -375,7 +375,7 @@
 	.owner		= THIS_MODULE,
 };
 
-int __init init_mtdblock(void)
+static int __init init_mtdblock(void)
 {
 	return register_mtd_blktrans(&mtdblock_tr);
 }
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/mtdblock_ro.c linux-2.4.30/drivers/mtd/mtdblock_ro.c
--- linux-2.4.30-orig/drivers/mtd/mtdblock_ro.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/mtdblock_ro.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: mtdblock_ro.c,v 1.18 2003/06/23 12:00:08 dwmw2 Exp $
+ * $Id: mtdblock_ro.c,v 1.2 2005/09/28 14:18:56 lel Exp $
  *
  * (C) 2003 David Woodhouse <dwmw2@infradead.org>
  *
@@ -58,7 +58,7 @@
 	kfree(dev);
 }
 
-struct mtd_blktrans_ops mtdblock_tr = {
+static struct mtd_blktrans_ops mtdblock_tr = {
 	.name		= "mtdblock",
 	.major		= 31,
 	.part_bits	= 0,
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/mtdchar.c linux-2.4.30/drivers/mtd/mtdchar.c
--- linux-2.4.30-orig/drivers/mtd/mtdchar.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/mtdchar.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: mtdchar.c,v 1.58 2004/04/20 12:22:35 gleixner Exp $
+ * $Id: mtdchar.c,v 1.4 2006/11/15 10:35:58 jok Exp $
  *
  * Character-device access to raw MTD devices.
  *
@@ -17,25 +17,77 @@
 
 #ifdef CONFIG_DEVFS_FS
 #include <linux/devfs_fs_kernel.h>
-static void mtd_notify_add(struct mtd_info* mtd);
-static void mtd_notify_remove(struct mtd_info* mtd);
+
+#if 0
+static void mtd_notify_add(struct mtd_info* mtd)
+{
+	if (!mtd)
+		return;
+
+#if 0
+	devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
+		      S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index);
+		
+	devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
+		      S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index);
+#endif
+}
+
+static void mtd_notify_remove(struct mtd_info* mtd)
+{
+	if (!mtd)
+		return;
+#if 0
+	devfs_remove("mtd/%d", mtd->index);
+	devfs_remove("mtd/%dro", mtd->index);
+#endif
+}
 
 static struct mtd_notifier notifier = {
 	.add	= mtd_notify_add,
 	.remove	= mtd_notify_remove,
 };
+#endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-static devfs_handle_t devfs_dir_handle;
-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
-static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES];
+static inline void mtdchar_devfs_init(void)
+{
+#if 0
+	devfs_mk_dir("mtd");
+	register_mtd_user(&notifier);
 #endif
+}
 
+static inline void mtdchar_devfs_exit(void)
+{
+#if 0
+	unregister_mtd_user(&notifier);
+	devfs_remove("mtd");
+#endif
+}
+#else /* !DEVFS */
+#define mtdchar_devfs_init() do { } while(0)
+#define mtdchar_devfs_exit() do { } while(0)
 #endif
 
+/*
+ * We use file->private_data to store a pointer to the MTDdevice.
+ * Since alighment is at least 32 bits, we have 2 bits free for OTP
+ * modes as well.
+ */
+
+#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L)
+
+#define MTD_MODE_OTP_FACT	1
+#define MTD_MODE_OTP_USER	2
+#define MTD_MODE(file)		((long)((file)->private_data) & 3)
+
+#define SET_MTD_MODE(file, mode) \
+	do { long __p = (long)((file)->private_data); \
+	     (file)->private_data = (void *)((__p & ~3L) | mode); } while (0)
+
 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
 {
-	struct mtd_info *mtd=(struct mtd_info *)file->private_data;
+	struct mtd_info *mtd = TO_MTD(file);
 
 	switch (orig) {
 	case 0:
@@ -108,7 +160,7 @@
 
 	DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 
-	mtd = (struct mtd_info *)file->private_data;
+	mtd = TO_MTD(file);
 	
 	if (mtd->sync)
 		mtd->sync(mtd);
@@ -123,9 +175,9 @@
 */
 #define MAX_KMALLOC_SIZE 0x20000
 
-static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos)
+static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
 {
-	struct mtd_info *mtd = (struct mtd_info *)file->private_data;
+	struct mtd_info *mtd = TO_MTD(file);
 	size_t retlen=0;
 	size_t total_retlen=0;
 	int ret=0;
@@ -152,8 +204,23 @@
 		if (!kbuf)
 			return -ENOMEM;
 		
-		ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
-		if (!ret) {
+		switch (MTD_MODE(file)) {
+		case MTD_MODE_OTP_FACT:
+			ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
+			break;
+		case MTD_MODE_OTP_USER:
+			ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
+			break;
+		default:
+			ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
+		}
+		/* Nand returns -EBADMSG on ecc errors, but it returns
+		 * the data. For our userspace tools it is important
+		 * to dump areas with ecc errors ! 
+		 * Userspace software which accesses NAND this way
+		 * must be aware of the fact that it deals with NAND
+		 */
+		if (!ret || (ret == -EBADMSG)) {
 			*ppos += retlen;
 			if (copy_to_user(buf, kbuf, retlen)) {
 			        kfree(kbuf);
@@ -164,6 +231,8 @@
 
 			count -= retlen;
 			buf += retlen;
+			if (retlen == 0)
+				count = 0;
 		}
 		else {
 			kfree(kbuf);
@@ -172,13 +241,13 @@
 		
 		kfree(kbuf);
 	}
-	
+
 	return total_retlen;
 } /* mtd_read */
 
-static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)
+static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
 {
-	struct mtd_info *mtd = (struct mtd_info *)file->private_data;
+	struct mtd_info *mtd = TO_MTD(file);
 	char *kbuf;
 	size_t retlen;
 	size_t total_retlen=0;
@@ -213,7 +282,20 @@
 			return -EFAULT;
 		}
 		
-	        ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
+		switch (MTD_MODE(file)) {
+		case MTD_MODE_OTP_FACT:
+			ret = -EROFS;
+			break;
+		case MTD_MODE_OTP_USER:
+			if (!mtd->write_user_prot_reg) {
+				ret = -EOPNOTSUPP;
+				break;
+			}
+			ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
+			break;
+		default:
+			ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
+		}
 		if (!ret) {
 			*ppos += retlen;
 			total_retlen += retlen;
@@ -236,7 +318,7 @@
     IOCTL calls for getting device parameters.
 
 ======================================================================*/
-static void mtd_erase_callback (struct erase_info *instr)
+static void mtdchar_erase_callback (struct erase_info *instr)
 {
 	wake_up((wait_queue_head_t *)instr->priv);
 }
@@ -244,7 +326,8 @@
 static int mtd_ioctl(struct inode *inode, struct file *file,
 		     u_int cmd, u_long arg)
 {
-	struct mtd_info *mtd = (struct mtd_info *)file->private_data;
+	struct mtd_info *mtd = TO_MTD(file);
+	void __user *argp = (void __user *)arg;
 	int ret = 0;
 	u_long size;
 	
@@ -252,17 +335,17 @@
 
 	size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
 	if (cmd & IOC_IN) {
-		ret = verify_area(VERIFY_READ, (char *)arg, size);
+		ret = verify_area(VERIFY_READ, argp, size);
 		if (ret) return ret;
 	}
 	if (cmd & IOC_OUT) {
-		ret = verify_area(VERIFY_WRITE, (char *)arg, size);
+		ret = verify_area(VERIFY_WRITE, argp, size);
 		if (ret) return ret;
 	}
 	
 	switch (cmd) {
 	case MEMGETREGIONCOUNT:
-		if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))
+		if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
 			return -EFAULT;
 		break;
 
@@ -270,24 +353,19 @@
 	{
 		struct region_info_user ur;
 
-		if (copy_from_user(	&ur, 
-					(struct region_info_user *)arg, 
-					sizeof(struct region_info_user))) {
+		if (copy_from_user(&ur, argp, sizeof(struct region_info_user)))
 			return -EFAULT;
-		}
 
 		if (ur.regionindex >= mtd->numeraseregions)
 			return -EINVAL;
-		if (copy_to_user((struct mtd_erase_region_info *) arg, 
-				&(mtd->eraseregions[ur.regionindex]),
+		if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]),
 				sizeof(struct mtd_erase_region_info)))
 			return -EFAULT;
 		break;
 	}
 
 	case MEMGETINFO:
-		if (copy_to_user((struct mtd_info *)arg, mtd,
-				 sizeof(struct mtd_info_user)))
+		if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user)))
 			return -EFAULT;
 		break;
 
@@ -308,13 +386,13 @@
 			init_waitqueue_head(&waitq);
 
 			memset (erase,0,sizeof(struct erase_info));
-			if (copy_from_user(&erase->addr, (u_long *)arg,
-					   2 * sizeof(u_long))) {
+			if (copy_from_user(&erase->addr, argp,
+				    sizeof(struct erase_info_user))) {
 				kfree(erase);
 				return -EFAULT;
 			}
 			erase->mtd = mtd;
-			erase->callback = mtd_erase_callback;
+			erase->callback = mtdchar_erase_callback;
 			erase->priv = (unsigned long)&waitq;
 			
 			/*
@@ -352,7 +430,7 @@
 		if(!(file->f_mode & 2))
 			return -EPERM;
 
-		if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
 			return -EFAULT;
 		
 		if (buf.length > 0x4096)
@@ -361,7 +439,7 @@
 		if (!mtd->write_oob)
 			ret = -EOPNOTSUPP;
 		else
-			ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
+			ret = verify_area(VERIFY_READ, buf.ptr, buf.length);
 
 		if (ret)
 			return ret;
@@ -377,7 +455,7 @@
 
 		ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
 
-		if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
+		if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t)))
 			ret = -EFAULT;
 
 		kfree(databuf);
@@ -391,7 +469,7 @@
 		void *databuf;
 		ssize_t retlen;
 
-		if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
 			return -EFAULT;
 		
 		if (buf.length > 0x4096)
@@ -400,7 +478,7 @@
 		if (!mtd->read_oob)
 			ret = -EOPNOTSUPP;
 		else
-			ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
+			ret = verify_area(VERIFY_WRITE, buf.ptr, buf.length);
 
 		if (ret)
 			return ret;
@@ -411,7 +489,7 @@
 		
 		ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
 
-		if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
+		if (put_user(retlen, (uint32_t __user *)argp))
 			ret = -EFAULT;
 		else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
 			ret = -EFAULT;
@@ -422,41 +500,169 @@
 
 	case MEMLOCK:
 	{
-		unsigned long adrs[2];
+		struct erase_info_user info;
 
-		if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
+		if (copy_from_user(&info, argp, sizeof(info)))
 			return -EFAULT;
 
 		if (!mtd->lock)
 			ret = -EOPNOTSUPP;
 		else
-			ret = mtd->lock(mtd, adrs[0], adrs[1]);
+			ret = mtd->lock(mtd, info.start, info.length);
 		break;
 	}
 
 	case MEMUNLOCK:
 	{
-		unsigned long adrs[2];
+		struct erase_info_user info;
 
-		if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
+		if (copy_from_user(&info, argp, sizeof(info)))
 			return -EFAULT;
 
 		if (!mtd->unlock)
 			ret = -EOPNOTSUPP;
 		else
-			ret = mtd->unlock(mtd, adrs[0], adrs[1]);
+			ret = mtd->unlock(mtd, info.start, info.length);
 		break;
 	}
 
 	case MEMSETOOBSEL:
 	{
-		if (copy_from_user(&mtd->oobinfo ,(void *)arg, sizeof(struct nand_oobinfo)))
+		if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo)))
 			return -EFAULT;
 		break;
 	}
+
+	case MEMGETOOBSEL:
+	{
+		if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo)))
+			return -EFAULT;
+		break;
+	}
+
+	case MEMGETBADBLOCK:
+	{
+		loff_t offs;
 		
+		if (copy_from_user(&offs, argp, sizeof(loff_t)))
+			return -EFAULT;
+		if (!mtd->block_isbad)
+			ret = -EOPNOTSUPP;
+		else
+			return mtd->block_isbad(mtd, offs);
+		break;
+	}
+
+	case MEMSETBADBLOCK:
+	{
+		loff_t offs;
+
+		if (copy_from_user(&offs, argp, sizeof(loff_t)))
+			return -EFAULT;
+		if (!mtd->block_markbad)
+			ret = -EOPNOTSUPP;
+		else
+			return mtd->block_markbad(mtd, offs);
+		break;
+	}
+
+#ifdef CONFIG_MTD_OTP
+	case OTPSELECT:
+	{
+		int mode;
+		if (copy_from_user(&mode, argp, sizeof(int)))
+			return -EFAULT;
+		SET_MTD_MODE(file, 0);
+		switch (mode) {
+		case MTD_OTP_FACTORY:
+			if (!mtd->read_fact_prot_reg)
+				ret = -EOPNOTSUPP;
+			else
+				SET_MTD_MODE(file, MTD_MODE_OTP_FACT);
+			break;
+		case MTD_OTP_USER:
+			if (!mtd->read_fact_prot_reg)
+				ret = -EOPNOTSUPP;
+			else
+				SET_MTD_MODE(file, MTD_MODE_OTP_USER);
+			break;
+		default:
+			ret = -EINVAL;
+		case MTD_OTP_OFF:
+			break;
+		}
+		break;
+	}
+
+	case OTPGETREGIONCOUNT:
+	case OTPGETREGIONINFO:
+	{
+		struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+		ret = -EOPNOTSUPP;
+		switch (MTD_MODE(file)) {
+		case MTD_MODE_OTP_FACT:
+			if (mtd->get_fact_prot_info)
+				ret = mtd->get_fact_prot_info(mtd, buf, 4096);
+			break;
+		case MTD_MODE_OTP_USER:
+			if (mtd->get_user_prot_info)
+				ret = mtd->get_user_prot_info(mtd, buf, 4096);
+			break;
+		}
+		if (ret >= 0) {
+			if (cmd == OTPGETREGIONCOUNT) {
+				int nbr = ret / sizeof(struct otp_info);
+				ret = copy_to_user(argp, &nbr, sizeof(int));
+			} else
+				ret = copy_to_user(argp, buf, ret);
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(buf);
+		break;
+	}
+
+	case OTPLOCK:
+	{
+		struct otp_info info;
+
+		if (MTD_MODE(file) != MTD_MODE_OTP_USER)
+			return -EINVAL;
+		if (copy_from_user(&info, argp, sizeof(info)))
+			return -EFAULT;
+		if (!mtd->lock_user_prot_reg)
+			return -EOPNOTSUPP;
+		ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
+		break;
+	}
+#endif
+
+	case 0x4711:
+	{
+	    int tmpMtdNumber;
+
+	    if (copy_from_user(&tmpMtdNumber, argp, sizeof(int)))
+		return -EFAULT;
+
+	    LockMtdDevice(tmpMtdNumber);
+	    
+	    break;
+	}
+	case 0x4712:
+	{
+	    int tmpMtdNumber;
+
+	    if (copy_from_user(&tmpMtdNumber, argp, sizeof(int)))
+		return -EFAULT;
+
+	    UnlockMtdDevice(tmpMtdNumber);
+	    
+	    break;
+	}
+
 	default:
-		DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
 		ret = -ENOTTY;
 	}
 
@@ -473,100 +679,21 @@
 	.release	= mtd_close,
 };
 
-
-#ifdef CONFIG_DEVFS_FS
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-
-/* Notification that a new device has been added. Create the devfs entry for
- * it. */
-
-static void mtd_notify_add(struct mtd_info* mtd)
-{
-	char name[8];
-
-	if (!mtd)
-		return;
-
-	sprintf(name, "%d", mtd->index);
-	devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
-			DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
-			S_IFCHR | S_IRUGO | S_IWUGO,
-			&mtd_fops, NULL);
-
-	sprintf(name, "%dro", mtd->index);
-	devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
-			DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
-			S_IFCHR | S_IRUGO,
-			&mtd_fops, NULL);
-}
-
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
-	if (!mtd)
-		return;
-
-	devfs_unregister(devfs_rw_handle[mtd->index]);
-	devfs_unregister(devfs_ro_handle[mtd->index]);
-}
-
-#else
-
-static void mtd_notify_add(struct mtd_info* mtd)
-{
-	if (!mtd)
-		return;
-
-	devfs_mk_cdev (MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
-	               S_IFCHR | S_IRUGO | S_IWUGO,
-	               "mtd/%d", mtd->index);
-		
-	devfs_mk_cdev (MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
-	               S_IFCHR | S_IRUGO,
-	               "mtd/%dro", mtd->index);
-}
-
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
-	if (!mtd)
-		return;
-
-	devfs_remove ("mtd/%d", mtd->index*2);
-	devfs_remove ("mtd/%dro", mtd->index*2);
-}
-
-#endif
-#endif
-
 static int __init init_mtdchar(void)
 {
-	if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
-	{
+	if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
 		printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
 		       MTD_CHAR_MAJOR);
 		return -EAGAIN;
 	}
 
-#ifdef CONFIG_DEVFS_FS
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-	devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
-#else	
-	devfs_mk_dir("mtd");
-#endif
-	register_mtd_user(&notifier);
-#endif
+	mtdchar_devfs_init();
 	return 0;
 }
 
 static void __exit cleanup_mtdchar(void)
 {
-#ifdef CONFIG_DEVFS_FS
-	unregister_mtd_user(&notifier);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-	devfs_unregister(devfs_dir_handle);
-#else	
-	devfs_remove ("mtd");
-#endif
-#endif
+	mtdchar_devfs_exit();
 	unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
 }
 
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/mtdconcat.c linux-2.4.30/drivers/mtd/mtdconcat.c
--- linux-2.4.30-orig/drivers/mtd/mtdconcat.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/mtdconcat.c	2006-12-11 18:18:37.000000000 +0100
@@ -7,7 +7,7 @@
  *
  * This code is GPL
  *
- * $Id: mtdconcat.c,v 1.8 2003/06/30 11:01:26 dwmw2 Exp $
+ * $Id: mtdconcat.c,v 1.2 2005/09/28 14:18:57 lel Exp $
  */
 
 #include <linux/module.h>
@@ -391,7 +391,7 @@
 	struct mtd_concat *concat = CONCAT(mtd);
 	struct mtd_info *subdev;
 	int i, err;
-	u_int32_t length;
+	u_int32_t length, offset = 0;
 	struct erase_info *erase;
 
 	if (!(mtd->flags & MTD_WRITEABLE))
@@ -450,6 +450,8 @@
 			return -EINVAL;
 	}
 
+	instr->fail_addr = 0xffffffff;
+
 	/* make a local copy of instr to avoid modifying the caller's struct */
 	erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
 
@@ -465,10 +467,12 @@
 	 */
 	for (i = 0; i < concat->num_subdev; i++) {
 		subdev = concat->subdev[i];
-		if (subdev->size <= erase->addr)
+		if (subdev->size <= erase->addr) {
 			erase->addr -= subdev->size;
-		else
+			offset += subdev->size;
+		} else {
 			break;
+		}
 	}
 
 	/* must never happen since size limit has been verified above */
@@ -497,6 +501,8 @@
 			 * block alignment has been checked above */
 			if (err == -EINVAL)
 				BUG();
+			if (erase->fail_addr != 0xffffffff)
+				instr->fail_addr = erase->fail_addr + offset;
 			break;
 		}
 		/*
@@ -508,12 +514,13 @@
 		 * current subdevice, i.e. at offset zero.
 		 */
 		erase->addr = 0;
+		offset += subdev->size;
 	}
+	instr->state = erase->state;
 	kfree(erase);
 	if (err)
 		return err;
 
-	instr->state = MTD_ERASE_DONE;
 	if (instr->callback)
 		instr->callback(instr);
 	return 0;
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/mtdcore.c linux-2.4.30/drivers/mtd/mtdcore.c
--- linux-2.4.30-orig/drivers/mtd/mtdcore.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/mtdcore.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: mtdcore.c,v 1.39 2003/05/21 15:15:03 dwmw2 Exp $
+ * $Id: mtdcore.c,v 1.3 2006/11/15 10:36:47 jok Exp $
  *
  * Core registration and callback routines for MTD
  * drivers and users.
@@ -35,6 +35,59 @@
 
 static LIST_HEAD(mtd_notifiers);
 
+
+/**
+ *
+ *
+ */
+void LockMtdDevice(int number)
+{
+    if ((number >= 0) && (number < MAX_MTD_DEVICES))
+    {
+	if (mtd_table[number])
+	{
+	    mtd_table[number]->flags &= ~MTD_WRITEABLE;
+	}
+    }
+}
+
+
+/**
+ *
+ *
+ */
+void UnlockMtdDevice(int number)
+{
+    if ((number >= 0) && (number < MAX_MTD_DEVICES))
+    {
+	if (mtd_table[number])
+	{
+	    mtd_table[number]->flags |= MTD_WRITEABLE;
+	}
+    }
+}
+
+
+/**
+ *
+ *
+ */
+void OccupyMtdNumber(int number)
+{
+    if (!mtd_table[number])
+	mtd_table[number] = 0x12345678;
+}
+
+/**
+ *
+ *
+ */
+void ReleaseMtdNumber(int number)
+{
+    mtd_table[number] = NULL;
+}
+  
+
 /**
  *	add_mtd_device - register an MTD device
  *	@mtd: pointer to new MTD device info structure
@@ -149,8 +202,8 @@
 }
 
 /**
- *	register_mtd_user - unregister a 'user' of MTD devices.
- *	@new: pointer to notifier info structure
+ *	unregister_mtd_user - unregister a 'user' of MTD devices.
+ *	@old: pointer to notifier info structure
  *
  *	Removes a callback function pair from the list of 'users' to be
  *	notified upon addition or removal of MTD devices. Causes the
@@ -231,7 +284,7 @@
  *			dont implement their own
  */
 
-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
+int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
 		       unsigned long count, loff_t to, size_t *retlen)
 {
 	unsigned long i;
@@ -261,7 +314,7 @@
  *		       implement their own
  */
 
-int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
+int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
 		      unsigned long count, loff_t from, size_t *retlen)
 {
 	unsigned long i;
@@ -333,10 +386,7 @@
 /* Support for /proc/mtd */
 
 #ifdef CONFIG_PROC_FS
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 static struct proc_dir_entry *proc_mtd;
-#endif
 
 static inline int mtd_proc_info (char *buf, int i)
 {
@@ -349,13 +399,8 @@
 		       this->erasesize, this->name);
 }
 
-static int mtd_read_proc ( char *page, char **start, off_t off,int count
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-                       ,int *eof, void *data_unused
-#else
-                        ,int unused
-#endif
-			)
+static int mtd_read_proc (char *page, char **start, off_t off, int count,
+			  int *eof, void *data_unused)
 {
 	int len, l, i;
         off_t   begin = 0;
@@ -375,9 +420,7 @@
                 }
         }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
         *eof = 1;
-#endif
 
 done:
 	up(&mtd_table_mutex);
@@ -387,36 +430,16 @@
         return ((count < begin+len-off) ? count : begin+len-off);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
-struct proc_dir_entry mtd_proc_entry = {
-        0,                 /* low_ino: the inode -- dynamic */
-        3, "mtd",     /* len of name and name */
-        S_IFREG | S_IRUGO, /* mode */
-        1, 0, 0,           /* nlinks, owner, group */
-        0, NULL,           /* size - unused; operations -- use default */
-        &mtd_read_proc,   /* function used to read data */
-        /* nothing more */
-    };
-#endif
-
 #endif /* CONFIG_PROC_FS */
 
 /*====================================================================*/
 /* Init code */
 
-int __init init_mtd(void)
+static int __init init_mtd(void)
 {
 #ifdef CONFIG_PROC_FS
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-	if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
-	  proc_mtd->read_proc = mtd_read_proc;
-#else
-        proc_register_dynamic(&proc_root,&mtd_proc_entry);
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < 0x20212
-	init_mtd_devices();
+	if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
+		proc_mtd->read_proc = mtd_read_proc;
 #endif
 
 #ifdef CONFIG_PM
@@ -435,12 +458,8 @@
 #endif
 
 #ifdef CONFIG_PROC_FS
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
         if (proc_mtd)
-          remove_proc_entry( "mtd", 0);
-#else
-        proc_unregister(&proc_root,mtd_proc_entry.low_ino);
-#endif
+		remove_proc_entry( "mtd", NULL);
 #endif
 }
 
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/mtdpart.c linux-2.4.30/drivers/mtd/mtdpart.c
--- linux-2.4.30-orig/drivers/mtd/mtdpart.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/mtdpart.c	2006-12-11 18:18:37.000000000 +0100
@@ -5,7 +5,7 @@
  *
  * This code is GPL
  *
- * $Id: mtdpart.c,v 1.44 2004/05/06 08:23:02 gleixner Exp $
+ * $Id: mtdpart.c,v 1.2 2005/09/28 14:18:58 lel Exp $
  *
  * 	02-21-2002	Thomas Gleixner <gleixner@autronix.de>
  *			added support for read_oob, write_oob
@@ -116,6 +116,13 @@
 					len, retlen, buf);
 }
 
+static int part_get_user_prot_info (struct mtd_info *mtd,
+				    struct otp_info *buf, size_t len)
+{
+	struct mtd_part *part = PART(mtd);
+	return part->master->get_user_prot_info (part->master, buf, len);
+}
+
 static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, 
 			size_t *retlen, u_char *buf)
 {
@@ -124,6 +131,13 @@
 					len, retlen, buf);
 }
 
+static int part_get_fact_prot_info (struct mtd_info *mtd,
+				    struct otp_info *buf, size_t len)
+{
+	struct mtd_part *part = PART(mtd);
+	return part->master->get_fact_prot_info (part->master, buf, len);
+}
+
 static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
 			size_t *retlen, const u_char *buf)
 {
@@ -182,7 +196,13 @@
 					len, retlen, buf);
 }
 
-static int part_writev (struct mtd_info *mtd,  const struct iovec *vecs,
+static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) 
+{
+	struct mtd_part *part = PART(mtd);
+	return part->master->lock_user_prot_reg (part->master, from, len);
+}
+
+static int part_writev (struct mtd_info *mtd,  const struct kvec *vecs,
 			 unsigned long count, loff_t to, size_t *retlen)
 {
 	struct mtd_part *part = PART(mtd);
@@ -197,7 +217,7 @@
 					NULL, &mtd->oobinfo);
 }
 
-static int part_readv (struct mtd_info *mtd,  struct iovec *vecs,
+static int part_readv (struct mtd_info *mtd,  struct kvec *vecs,
 			 unsigned long count, loff_t from, size_t *retlen)
 {
 	struct mtd_part *part = PART(mtd);
@@ -210,7 +230,7 @@
 					NULL, &mtd->oobinfo);
 }
 
-static int part_writev_ecc (struct mtd_info *mtd,  const struct iovec *vecs,
+static int part_writev_ecc (struct mtd_info *mtd,  const struct kvec *vecs,
 			 unsigned long count, loff_t to, size_t *retlen,
 			 u_char *eccbuf,  struct nand_oobinfo *oobsel)
 {
@@ -224,7 +244,7 @@
 					eccbuf, oobsel);
 }
 
-static int part_readv_ecc (struct mtd_info *mtd,  struct iovec *vecs,
+static int part_readv_ecc (struct mtd_info *mtd,  struct kvec *vecs,
 			 unsigned long count, loff_t from, size_t *retlen,
 			 u_char *eccbuf,  struct nand_oobinfo *oobsel)
 {
@@ -239,13 +259,29 @@
 static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
 {
 	struct mtd_part *part = PART(mtd);
+	int ret;
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
 	if (instr->addr >= mtd->size)
 		return -EINVAL;
 	instr->addr += part->offset;
-	return part->master->erase(part->master, instr);
+	ret = part->master->erase(part->master, instr);
+	return ret;
+}
+
+void mtd_erase_callback(struct erase_info *instr)
+{
+	if (instr->mtd->erase == part_erase) {
+		struct mtd_part *part = PART(instr->mtd);
+
+		if (instr->fail_addr != 0xffffffff)
+			instr->fail_addr -= part->offset;
+		instr->addr -= part->offset;
+	}
+	if (instr->callback)
+		instr->callback(instr);
 }
+EXPORT_SYMBOL_GPL(mtd_erase_callback);
 
 static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
 {
@@ -393,6 +429,12 @@
 			slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
 		if(master->write_user_prot_reg)
 			slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
+		if(master->lock_user_prot_reg)
+			slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
+		if(master->get_user_prot_info)
+			slave->mtd.get_user_prot_info = part_get_user_prot_info;
+		if(master->get_fact_prot_info)
+			slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
 		if (master->sync)
 			slave->mtd.sync = part_sync;
 		if (!i && master->suspend && master->resume) {
@@ -507,10 +549,10 @@
 EXPORT_SYMBOL(add_mtd_partitions);
 EXPORT_SYMBOL(del_mtd_partitions);
 
-static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(part_parser_lock);
 static LIST_HEAD(part_parsers);
 
-struct mtd_part_parser *get_partition_parser(const char *name)
+static struct mtd_part_parser *get_partition_parser(const char *name)
 {
 	struct list_head *this;
 	void *ret = NULL;
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/Config.in linux-2.4.30/drivers/mtd/nand/Config.in
--- linux-2.4.30-orig/drivers/mtd/nand/Config.in	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/drivers/mtd/nand/Config.in	2006-12-11 18:18:37.000000000 +0100
@@ -1,6 +1,6 @@
 # drivers/mtd/nand/Config.in
 
-# $Id: Config.in,v 1.17 2004/05/10 07:28:18 ppopov Exp $
+# $Id: Config.in,v 1.2 2005/09/28 14:19:32 lel Exp $
 
 mainmenu_option next_comment
 
@@ -38,7 +38,30 @@
     dep_tristate '  NAND Flash device on PPChameleonEVB board' CONFIG_MTD_NAND_PPCHAMELEONEVB $CONFIG_MTD_NAND
 fi
 
-if [ "$CONFIG_SOC_AU1550" = "y" -o "$CONFIG_SOC_AU1200" = "y" ]; then
-   dep_tristate '  NAND Flash Driver for Au1550/Au1200 controller' CONFIG_MTD_NAND_AU1550 $CONFIG_MTD_NAND 
+if [ "$CONFIG_SOC_AU1550" = "y" ]; then
+   dep_tristate '  NAND Flash Driver for Au1550 controller' CONFIG_MTD_NAND_AU1550 $CONFIG_MTD_NAND 
 fi
+
+#!!
+if [ "$CONFIG_SH_SOLUTION_ENGINE" = "y" ]; then
+   dep_tristate '  Renesas Flash ROM 4-slot interface board (FROM_BOARD4)' CONFIG_MTD_NAND_RTC_FROM4 $CONFIG_MTD_NAND
+   if [ "$CONFIG_MTD_NAND_RTC_FROM4" = "y" ]; then 
+      define_bool CONFIG_REED_SOLOMON y
+      define_bool CONFIG_REED_SOLOMON_DEC8 y
+   else
+      if [ "$CONFIG_MTD_NAND_RTC_FROM4" = "m" ]; then 
+         define_bool CONFIG_REED_SOLOMON m
+         define_bool CONFIG_REED_SOLOMON_DEC8 m
+      fi
+   fi
+fi
+
+dep_tristate '  DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)' CONFIG_MTD_NAND_DISKONCHIP $CONFIG_MTD_NAND $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_MTD_NAND_DISKONCHIP" = "y" -o "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then
+   bool '    Advanced detection options for DiskOnChip' CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED
+   hex  '    Physical address of DiskOnChip' CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 
+   bool '    Probe high addresses' CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH $CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED
+   bool '    Allow BBT write on DiskOnChip Millennium and 2000TSOP' CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE
+fi
+
 endmenu
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/Makefile linux-2.4.30/drivers/mtd/nand/Makefile
--- linux-2.4.30-orig/drivers/mtd/nand/Makefile	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/nand/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -1,22 +1,16 @@
 #
-# linux/drivers/nand/Makefile
+# linux/drivers/nand/Makefile.24
+# Makefile for obsolete kernels.
 #
-# $Id: Makefile.common,v 1.6 2004/03/18 09:28:19 gleixner Exp $
+# $Id: Makefile,v 1.2 2005/09/28 14:19:33 lel Exp $
 
-ifeq ($(PATCHLEVEL),4)
-O_TARGET			:= nandlink.o
-export-objs			:= nand.o nand_ecc.o nand_ids.o
-endif
+O_TARGET	:= nandlink.o
+export-objs	:= nand_base.o nand_bbt.o nand_ecc.o nand_ids.o
+list-multi	:= nand.o
 
-obj-$(CONFIG_MTD_NAND)		+= nand.o nand_ecc.o
-obj-$(CONFIG_MTD_NAND_SPIA)	+= spia.o
-obj-$(CONFIG_MTD_NAND_TOTO)	+= toto.o
-obj-$(CONFIG_MTD_NAND_AUTCPU12)	+= autcpu12.o
-obj-$(CONFIG_MTD_NAND_EDB7312)  += edb7312.o
-obj-$(CONFIG_MTD_NAND_TX4925NDFMC)  += tx4925ndfmc.o
-obj-$(CONFIG_MTD_NAND_TX4938NDFMC)  += tx4938ndfmc.o
-obj-$(CONFIG_MTD_NAND_AU1550)	  += au1550nd.o
-obj-$(CONFIG_MTD_NAND_IDS)	+= nand_ids.o
-obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB)   += ppchameleonevb.o
+include Makefile.common
 
--include $(TOPDIR)/Rules.make
+include $(TOPDIR)/Rules.make
+
+nand.o: $(nand-objs)
+	$(LD) -r -o $@ $(nand-objs)
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/Makefile.common linux-2.4.30/drivers/mtd/nand/Makefile.common
--- linux-2.4.30-orig/drivers/mtd/nand/Makefile.common	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/nand/Makefile.common	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,24 @@
+#
+# linux/drivers/nand/Makefile
+#
+# $Id: Makefile.common,v 1.2 2005/09/28 14:19:33 lel Exp $
+
+obj-$(CONFIG_MTD_NAND)			+= nand.o nand_ecc.o
+obj-$(CONFIG_MTD_NAND_IDS)		+= nand_ids.o
+
+obj-$(CONFIG_MTD_NAND_SPIA)		+= spia.o
+obj-$(CONFIG_MTD_NAND_TOTO)		+= toto.o
+obj-$(CONFIG_MTD_NAND_AUTCPU12)		+= autcpu12.o
+obj-$(CONFIG_MTD_NAND_EDB7312)		+= edb7312.o
+obj-$(CONFIG_MTD_NAND_TX4925NDFMC)	+= tx4925ndfmc.o
+obj-$(CONFIG_MTD_NAND_TX4938NDFMC)	+= tx4938ndfmc.o
+obj-$(CONFIG_MTD_NAND_AU1550)		+= au1550nd.o
+obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB)	+= ppchameleonevb.o
+obj-$(CONFIG_MTD_NAND_S3C2410)		+= s3c2410.o
+obj-$(CONFIG_MTD_NAND_DISKONCHIP)	+= diskonchip.o
+obj-$(CONFIG_MTD_NAND_H1900)		+= h1910.o
+obj-$(CONFIG_MTD_NAND_RTC_FROM4)	+= rtc_from4.o
+obj-$(CONFIG_MTD_NAND_SHARPSL)		+= sharpsl.o
+obj-$(CONFIG_MTD_NAND_NANDSIM)		+= nandsim.o
+
+nand-objs = nand_base.o nand_bbt.o
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/au1550nd.c linux-2.4.30/drivers/mtd/nand/au1550nd.c
--- linux-2.4.30-orig/drivers/mtd/nand/au1550nd.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/drivers/mtd/nand/au1550nd.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,7 +3,7 @@
  *
  *  Copyright (C) 2004 Embedded Edge, LLC
  *
- * $Id: au1550nd.c,v 1.4 2004/05/12 06:23:58 ppopov Exp $
+ * $Id: au1550nd.c,v 1.5 2005/10/14 11:24:32 lel Exp $
  *
  * 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
@@ -18,238 +18,532 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <asm/io.h>
-#include <asm/au1000.h>
 
-#ifdef CONFIG_MIPS_PB1550
-#include <asm/pb1550.h> 
-#define NAND_STCFG  0x00400005 /* 8-bit NAND */
-#define NAND_STTIME 0x00007774 /* valid for 396MHz SD=2 only */
-#define NAND_STADDR 0x12000FFF /* physical address 0x20000000 */
+#undef AU_NAND_DISABLE_INTERRUPTS
+
+#ifdef AU_NAND_DISABLE_INTERRUPTS
+#include <linux/interrupt.h>
 #endif
 
+/* fixme: this is ugly */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0)
+#include <asm/mach-au1x00/au1000.h>
+#ifdef CONFIG_MIPS_PB1550
+#include <asm/mach-pb1x00/pb1550.h> 
+#endif
 #ifdef CONFIG_MIPS_DB1550
-#define NAND_CS 1
-#define NAND_STCFG  0x00400005 /* 8-bit NAND */
-#define NAND_STTIME 0x00007774 /* valid for 396MHz SD=2 only */
-#define NAND_STADDR 0x12000FFF /* physical address 0x20000000 */
+#include <asm/mach-db1x00/db1x00.h> 
 #endif
-
-#ifdef CONFIG_MIPS_PB1200
-#define NAND_CS 1
+#else /* LINUX_VERSION ... */
+#include <asm/au1000.h>
+#ifdef CONFIG_MIPS_PB1550
+#include <asm/pb1550.h> 
 #endif
-
-#ifdef CONFIG_MIPS_DB1200
-#define NAND_CS 1
+#ifdef CONFIG_MIPS_DB1550
+#include <asm/db1x00.h> 
+#endif
+#ifdef CONFIG_MIPS_I3_MMB3
+#include <asm/mood300.h>
 #endif
-
-#ifndef NAND_CS
-#error "NAND_CS must be defined as 0, 1, 2 or 3, as appropriate"
 #endif
-
 
 /*
  * MTD structure for NAND controller
  */
-static struct mtd_info *au1xxx_nand_mtd = NULL;
-static volatile u32 p_nand;
-static int nand_width = 1; /* default, only x8 supported for now */
-
-/* Internal buffers. Page buffer and oob buffer for one block*/
-static u_char data_buf[512 + 16];
-static u_char oob_buf[16 * 32];
+static struct mtd_info *au1550_mtd = NULL;
+static void __iomem *p_nand;
+static int nand_width = 1; /* default x8*/
 
 /*
  * Define partitions for flash device
  */
 const static struct mtd_partition partition_info[] = {
+#ifndef CONFIG_MIPS_I3_MMB3 
 	{ 
-		.name = "NAND FS 0",
+		.name 	= "NAND FS 0",
+	  	.offset = 0,
+	  	.size 	= 8*1024*1024 
+	},
+	{ 
+		.name	= "NAND FS 1",
+		.offset =  MTDPART_OFS_APPEND,
+ 		.size	=    MTDPART_SIZ_FULL
+	}
+#else
+        { 
+		.name = "Mood 300 NAND FS 0",
 	  	.offset = 0,
-	  	.size = 8*1024*1024 
+	  	.size = 4*1024*1024 
 	},
 	{ 
-		.name = "NAND FS 1",
+		.name = "Mood 300 NAND FS 1",
 		.offset =  MTDPART_OFS_APPEND,
  		.size =    MTDPART_SIZ_FULL
 	}
+#endif
 };
 #define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
 
-static inline void write_cmd_reg(u8 cmd)
+
+/**
+ * au_nand_select_chip_override - [DEFAULT] control CE line
+ * @mtd:        MTD device structure
+ * @chip:       chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void au_nand_select_chip_override(struct mtd_info *mtd, int chip)
 {
-	if (nand_width)
-		*((volatile u8 *)(p_nand + MEM_STNAND_CMD)) = cmd;
-	else
-		*((volatile u16 *)(p_nand + MEM_STNAND_CMD)) = cmd;
+        struct nand_chip *this = mtd->priv;
+        switch(chip) {
+        case -1:
+                this->hwcontrol(mtd, NAND_CTL_CLRNCE);
+                break;
+        case 0:
+                this->hwcontrol(mtd, NAND_CTL_SETNCE);
+                break;
+
+        default:
+                BUG();
+        }
+}
+
+/* 
+ * Wait for the ready pin, after a command
+ * The timeout is catched later.
+ */
+static void nand_wait_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	unsigned long	timeo = jiffies + 2;
+
+	/* wait until command is processed or timeout occures */
+	do {
+		if (this->dev_ready(mtd))
+			return;
+	} while (time_before(jiffies, timeo));	
+}
+
+/**
+ * au_read_byte -  read one byte from the chip
+ * @mtd:	MTD device structure
+ *
+ *  read function for 8bit buswith
+ */
+static u_char au_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	u_char ret = readb(this->IO_ADDR_R);
 	au_sync();
+	return ret;
 }
 
-static inline void write_addr_reg(u8 addr)
+/**
+ * au_write_byte -  write one byte to the chip
+ * @mtd:	MTD device structure
+ * @byte:	pointer to data byte to write
+ *
+ *  write function for 8it buswith
+ */
+static void au_write_byte(struct mtd_info *mtd, u_char byte)
 {
-	if (nand_width)
-		*((volatile u8 *)(p_nand + MEM_STNAND_ADDR)) = addr;
-	else
-		*((volatile u16 *)(p_nand + MEM_STNAND_ADDR)) = addr;
+	struct nand_chip *this = mtd->priv;
+	writeb(byte, this->IO_ADDR_W);
 	au_sync();
 }
 
-static inline void write_data_reg(u8 data)
+/**
+ * au_read_byte16 -  read one byte endianess aware from the chip
+ * @mtd:	MTD device structure
+ *
+ *  read function for 16bit buswith with 
+ * endianess conversion
+ */
+static u_char au_read_byte16(struct mtd_info *mtd)
 {
-	if (nand_width)
-		*((volatile u8 *)(p_nand + MEM_STNAND_DATA)) = data;
-	else
-		*((volatile u16 *)(p_nand + MEM_STNAND_DATA)) = data;
+	struct nand_chip *this = mtd->priv;
+	u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
 	au_sync();
+	return ret;
 }
 
-static inline u32 read_data_reg(void)
+/**
+ * au_write_byte16 -  write one byte endianess aware to the chip
+ * @mtd:	MTD device structure
+ * @byte:	pointer to data byte to write
+ *
+ *  write function for 16bit buswith with
+ * endianess conversion
+ */
+static void au_write_byte16(struct mtd_info *mtd, u_char byte)
 {
-	u32 data;
-	if (nand_width) {
-		data = *((volatile u8 *)(p_nand + MEM_STNAND_DATA));
-		au_sync();
-	}
-	else {
-		data = *((volatile u16 *)(p_nand + MEM_STNAND_DATA));
-		au_sync();
-	}
-	return data;
+	struct nand_chip *this = mtd->priv;
+	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+	au_sync();
 }
 
-void au1xxx_hwcontrol(struct mtd_info *mtd, int cmd)
+/**
+ * au_read_word -  read one word from the chip
+ * @mtd:	MTD device structure
+ *
+ *  read function for 16bit buswith without 
+ * endianess conversion
+ */
+static u16 au_read_word(struct mtd_info *mtd)
 {
+	struct nand_chip *this = mtd->priv;
+	u16 ret = readw(this->IO_ADDR_R);
+	au_sync();
+	return ret;
 }
 
-int au1xxx_device_ready(struct mtd_info *mtd)
+/**
+ * au_write_word -  write one word to the chip
+ * @mtd:	MTD device structure
+ * @word:	data word to write
+ *
+ *  write function for 16bit buswith without 
+ * endianess conversion
+ */
+static void au_write_word(struct mtd_info *mtd, u16 word)
 {
-	int ready;
-	ready = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
-	return ready;
+	struct nand_chip *this = mtd->priv;
+	writew(word, this->IO_ADDR_W);
+	au_sync();
 }
 
-static u_char au1xxx_nand_read_byte(struct mtd_info *mtd)
+/**
+ * au_write_buf -  write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ *  write function for 8bit buswith
+ */
+static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
-	u_char ret;
-	ret = read_data_reg();
-	return ret;
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++) {
+		writeb(buf[i], this->IO_ADDR_W);
+		au_sync();
+	}
 }
 
-static void au1xxx_nand_write_byte(struct mtd_info *mtd, u_char byte)
+/**
+ * au_read_buf -  read chip data into buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ *  read function for 8bit buswith
+ */
+static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
 {
-	write_data_reg((u8)byte);
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++) {
+		buf[i] = readb(this->IO_ADDR_R);
+		au_sync();	
+	}
 }
 
-static void 
-au1xxx_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+/**
+ * au_verify_buf -  Verify chip data against buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ *  verify function for 8bit buswith
+ */
+static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 	int i;
+	struct nand_chip *this = mtd->priv;
 
-	for (i=0; i<len; i++)
-		write_data_reg(buf[i]);
+	for (i=0; i<len; i++) {
+		if (buf[i] != readb(this->IO_ADDR_R))
+			return -EFAULT;
+		au_sync();
+	}
+
+	return 0;
 }
 
-static void 
-au1xxx_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+/**
+ * au_write_buf16 -  write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ *  write function for 16bit buswith
+ */
+static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
 {
 	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+	
+	for (i=0; i<len; i++) {
+		writew(p[i], this->IO_ADDR_W);
+		au_sync();
+	}
+		
+}
+
+/**
+ * au_read_buf16 -  read chip data into buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ *  read function for 16bit buswith
+ */
+static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
 
-	for (i=0; i<len; i++)
-		buf[i] = (u_char)read_data_reg();
+	for (i=0; i<len; i++) {
+		p[i] = readw(this->IO_ADDR_R);
+		au_sync();
+	}
 }
 
-static int 
-au1xxx_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+/**
+ * au_verify_buf16 -  Verify chip data against buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ *  verify function for 16bit buswith
+ */
+static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
 {
 	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
 
-	for (i=0; i<len; i++)
-		if (buf[i] != (u_char)read_data_reg())
+	for (i=0; i<len; i++) {
+		if (p[i] != readw(this->IO_ADDR_R))
 			return -EFAULT;
-
+		au_sync();
+	}
 	return 0;
 }
 
-static void au1xxx_nand_select_chip(struct mtd_info *mtd, int chip)
+
+static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
 {
-	switch(chip) {
-	case -1:
-		/* deassert chip enable */
-		au_writel(au_readl(MEM_STNDCTL) & ~(1<<(4+NAND_CS)), MEM_STNDCTL);
+	register struct nand_chip *this = mtd->priv;
+
+	switch(cmd){
+
+	case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break;
+	case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break;
+
+	case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break;
+	case NAND_CTL_CLRALE: 
+		this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; 
+		/* FIXME: Nobody knows why this is neccecary, 
+		 * but it works only that way */
+		udelay(1); 
 		break;
-	case 0:
+
+	case NAND_CTL_SETNCE: 
 		/* assert (force assert) chip enable */
-		au_writel(au_readl(MEM_STNDCTL) | (1<<(4+NAND_CS)) , MEM_STNDCTL);
+		au_writel((1<<(4+NAND_CS)) , MEM_STNDCTL); break;
 		break;
 
-	default:
-		BUG();
+	case NAND_CTL_CLRNCE: 
+ 		/* deassert chip enable */
+		au_writel(0, MEM_STNDCTL); break;
+		break;
 	}
+
+	this->IO_ADDR_R = this->IO_ADDR_W;
+	
+	/* Drain the writebuffer */
+	au_sync();
 }
 
-static void au1xxx_nand_command (struct mtd_info *mtd, unsigned command, 
-		int column, int page_addr)
+int au1550_device_ready(struct mtd_info *mtd)
 {
-	register struct nand_chip *this = mtd->priv;
+	int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
+	au_sync();
+	return ret;
+}
 
-	/*
-	 * Write out the command to the device.
-	 */
-	if (command == NAND_CMD_SEQIN) {
-		int readcmd;
-
-		if (column >= mtd->oobblock) {
-			/* OOB area */
-			column -= mtd->oobblock;
-			readcmd = NAND_CMD_READOOB;
-		} else if (column < 256) {
-			/* First 256 bytes --> READ0 */
-			readcmd = NAND_CMD_READ0;
-		} else {
-			column -= 256;
-			readcmd = NAND_CMD_READ1;
-		}
-		write_cmd_reg(readcmd);
-	}
-	write_cmd_reg(command);
-
-	if (column != -1 || page_addr != -1) {
-
-		/* Serially input address */
-		if (column != -1)
-			write_addr_reg(column);
-		if (page_addr != -1) {
-			write_addr_reg((unsigned char) (page_addr & 0xff));
-			write_addr_reg(((page_addr >> 8) & 0xff));
-			/* One more address cycle for higher density devices */
-			if (mtd->size & 0x0c000000) 
-				write_addr_reg((unsigned char) ((page_addr >> 16) & 0x0f));
-		}
-	}
-	
-	switch (command) {
-			
-	case NAND_CMD_PAGEPROG:
-	case NAND_CMD_ERASE1:
-	case NAND_CMD_ERASE2:
-	case NAND_CMD_SEQIN:
-	case NAND_CMD_STATUS:
-		break;
 
-	case NAND_CMD_RESET:
-		if (this->dev_ready)	
-			break;
-		udelay(this->chip_delay);
-		write_cmd_reg(NAND_CMD_STATUS);
-		while ( !(read_data_reg() & 0x40));
-		return;
+/**
+ * au_select_chip - Do not drive CS manual by default.
+ *      Nand flash controller do that automatically.
+        We use manual CS drive only in NAND_CMD_READ0 and NAND_CMD_READOOB.
+ * @mtd:        MTD device structure
+ * @chip:       chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void au_select_chip(struct mtd_info *mtd, int chip)
+{
 
-	/* This applies to read commands */	
+}
+
+/**
+ * nand_command - [DEFAULT] Send command to NAND device
+ * @mtd:        MTD device structure
+ * @command:    the command to be sent
+ * @column:     the column address for this command, -1 if none
+ * @page_addr:  the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This function is used for small page
+ * devices (256/512 Bytes per page)
+ */
+static void au_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+        register struct nand_chip *this = mtd->priv;
+#ifdef AU_NAND_DISABLE_INTERRUPTS
+        unsigned long g_cpu_flags_cmd = 0;
+#endif
+	int cmd_is_read = 0;
+
+	/* Decide wether command needs to force-enable CS or not */
+	switch (command) {
+	case NAND_CMD_READ0:
+	case NAND_CMD_READ1:
+	case NAND_CMD_READOOB:
+	case NAND_CMD_READID:
+		cmd_is_read = 1;
+		break;
 	default:
-		udelay (this->chip_delay);
+		break;
 	}
-	
-	/* wait until command is processed */
-	while (!this->dev_ready(mtd));
+
+        /* Begin command latch cycle */
+        this->hwcontrol(mtd, NAND_CTL_SETCLE);
+        /*
+         * Write out the command to the device.
+         */
+        if (command == NAND_CMD_SEQIN) {
+                int readcmd;
+
+                if (column >= mtd->oobblock) {
+                        /* OOB area */
+                        column -= mtd->oobblock;
+                        readcmd = NAND_CMD_READOOB;
+                } else if (column < 256) {
+                        /* First 256 bytes --> READ0 */
+                        readcmd = NAND_CMD_READ0;
+                } else {
+                        column -= 256;
+                        readcmd = NAND_CMD_READ1;
+                }
+                this->write_byte(mtd, readcmd);
+        }
+        this->write_byte(mtd, command);
+
+        /* Set ALE and clear CLE to start address cycle */
+        this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+        if (column != -1 || page_addr != -1) {
+                this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+                /* Serially input address */
+                if (column != -1) {
+                        /* Adjust columns for 16 bit buswidth */
+                        if (this->options & NAND_BUSWIDTH_16) {
+                                column >>= 1;
+			}
+                        if( cmd_is_read ) {
+#ifdef AU_NAND_DISABLE_INTERRUPTS
+                                save_flags(g_cpu_flags_cmd);
+                                cli();
+#endif
+                                au_nand_select_chip_override( mtd, 0 );
+			}
+                        this->write_byte(mtd, column);
+                }
+                if (page_addr != -1) {
+                        this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+                        this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+
+                        if( cmd_is_read ){
+#ifdef AU_NAND_DISABLE_INTERRUPTS
+                                save_flags(g_cpu_flags_cmd);
+                                cli();
+#endif
+                                au_nand_select_chip_override( mtd, 0 );
+                        }
+
+
+                        /* One more address cycle for devices > 32MiB */
+                        if (this->chipsize > (32 << 20))
+                                this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
+                }
+                /* Latch in address */
+                this->hwcontrol(mtd, NAND_CTL_CLRALE);
+        }
+
+        /*
+         * program and erase have their own busy handlers
+         * status and sequential in needs no delay
+        */
+        switch (command) {
+
+        case NAND_CMD_PAGEPROG:
+        case NAND_CMD_ERASE1:
+        case NAND_CMD_ERASE2:
+        case NAND_CMD_SEQIN:
+        case NAND_CMD_STATUS:
+                return;
+
+        case NAND_CMD_RESET:
+                if (this->dev_ready)
+                        break;
+                udelay(this->chip_delay);
+                this->hwcontrol(mtd, NAND_CTL_SETCLE);
+                this->write_byte(mtd, NAND_CMD_STATUS);
+                this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+                while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
+                return;
+
+        /* This applies to read commands */
+        default:
+                /*
+                 * If we don't have access to the busy pin, we apply the given
+                 * command delay
+                */
+
+                if (!this->dev_ready) {
+                        udelay (this->chip_delay);
+                        if( cmd_is_read ) {
+                                goto au1550_nand_restore_flags;
+			}
+                        return;
+                }
+        }
+        /* Apply this short delay always to ensure that we do wait tWB in
+         * any case on any machine. */
+        ndelay (100);
+
+        nand_wait_ready(mtd);
+
+        if( cmd_is_read )
+                goto au1550_nand_restore_flags;
+
+        return;
+
+au1550_nand_restore_flags:
+        au_nand_select_chip_override( mtd, -1 );
+#ifdef AU_NAND_DISABLE_INTERRUPTS
+        restore_flags(g_cpu_flags_cmd);
+#endif
 }
 
 
@@ -259,31 +553,35 @@
 int __init au1xxx_nand_init (void)
 {
 	struct nand_chip *this;
+#ifdef CONFIG_MIPS_PB1550
 	u16 boot_swapboot = 0; /* default value */
+#endif
+	int retval;
 	u32 mem_staddr;
 	u32 nand_phys;
 
 	/* Allocate memory for MTD device structure and private data */
-	au1xxx_nand_mtd = kmalloc (sizeof(struct mtd_info) + 
+	au1550_mtd = kmalloc (sizeof(struct mtd_info) + 
 			sizeof (struct nand_chip), GFP_KERNEL);
-	if (!au1xxx_nand_mtd) {
+	if (!au1550_mtd) {
 		printk ("Unable to allocate NAND MTD dev structure.\n");
 		return -ENOMEM;
 	}
 
 	/* Get pointer to private data */
-	this = (struct nand_chip *) (&au1xxx_nand_mtd[1]);
+	this = (struct nand_chip *) (&au1550_mtd[1]);
 
 	/* Initialize structures */
-	memset((char *) au1xxx_nand_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) au1550_mtd, 0, sizeof(struct mtd_info));
 	memset((char *) this, 0, sizeof(struct nand_chip));
 
 	/* Link the private data with the MTD structure */
-	au1xxx_nand_mtd->priv = this;
+	au1550_mtd->priv = this;
+
 
 	/* disable interrupts */
 	au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL);
-
+ 
 	/* disable NAND boot */
 	au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL);
 
@@ -291,7 +589,6 @@
 	/* set gpio206 high */
 	au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR);
 
-	/* Pb1550: check boot configuration to make sure NAND is visible */
 	boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) | 
 		((bcsr->status >> 6)  & 0x1);
 	switch (boot_swapboot) {
@@ -302,7 +599,6 @@
 		case 0xD:
 			/* x16 NAND Flash */
 			nand_width = 0;
-			printk("Pb1550 NAND: 16-bit NAND not supported by MTD\n");
 			break;
 		case 1:
 		case 9:
@@ -314,8 +610,8 @@
 			break;
 		default:
 			printk("Pb1550 NAND: bad boot:swap\n");
-			kfree(au1xxx_nand_mtd);
-			return 1;
+			retval = -EINVAL;
+			goto outmem;
 	}
 #endif
 
@@ -342,29 +638,26 @@
 		au_writel(NAND_STADDR, MEM_STADDR3);
 	}
 #endif
-
-	/* Locate NAND chip-select in order to determine NAND physical address */
+ 
+	/* Locate NAND chip-select in order to determine NAND phys address */
 	mem_staddr = 0x00000000;
 	if (((au_readl(MEM_STCFG0) & 0x7) == 0x5) && (NAND_CS == 0))
 		mem_staddr = au_readl(MEM_STADDR0);
-	else
-	if (((au_readl(MEM_STCFG1) & 0x7) == 0x5) && (NAND_CS == 1))
+	else if (((au_readl(MEM_STCFG1) & 0x7) == 0x5) && (NAND_CS == 1))
 		mem_staddr = au_readl(MEM_STADDR1);
-	else
-	if (((au_readl(MEM_STCFG2) & 0x7) == 0x5) && (NAND_CS == 2))
+	else if (((au_readl(MEM_STCFG2) & 0x7) == 0x5) && (NAND_CS == 2))
 		mem_staddr = au_readl(MEM_STADDR2);
-	else
-	if (((au_readl(MEM_STCFG3) & 0x7) == 0x5) && (NAND_CS == 3))
+	else if (((au_readl(MEM_STCFG3) & 0x7) == 0x5) && (NAND_CS == 3))
 		mem_staddr = au_readl(MEM_STADDR3);
 
 	if (mem_staddr == 0x00000000) {
 		printk("Au1xxx NAND: ERROR WITH NAND CHIP-SELECT\n");
-		kfree(au1xxx_nand_mtd);
+		kfree(au1550_mtd);
 		return 1;
 	}
 	nand_phys = (mem_staddr << 4) & 0xFFFC0000;
 
-	p_nand = (volatile struct nand_regs *)ioremap(nand_phys, 0x1000);
+	p_nand = (void __iomem *)ioremap(nand_phys, 0x1000);
 
 	/* make controller and MTD agree */
 	if (NAND_CS == 0)
@@ -376,35 +669,51 @@
 	if (NAND_CS == 3)
 		nand_width = au_readl(MEM_STCFG3) & (1<<22);
 
+
 	/* Set address of hardware control function */
-	this->hwcontrol = au1xxx_hwcontrol;
-	this->dev_ready = au1xxx_device_ready;
+	this->hwcontrol = au1550_hwcontrol;
+	this->dev_ready = au1550_device_ready;
 	/* 30 us command delay time */
 	this->chip_delay = 30;		
-
-	this->cmdfunc = au1xxx_nand_command;
-	this->select_chip = au1xxx_nand_select_chip;
-	this->write_byte = au1xxx_nand_write_byte;
-	this->read_byte = au1xxx_nand_read_byte;
-	this->write_buf = au1xxx_nand_write_buf;
-	this->read_buf = au1xxx_nand_read_buf;
-	this->verify_buf = au1xxx_nand_verify_buf;
 	this->eccmode = NAND_ECC_SOFT;
 
-	/* Set internal data buffer */
-	this->data_buf = data_buf;
-	this->oob_buf = oob_buf;
+	this->options = NAND_NO_AUTOINCR;
+
+	if (!nand_width)
+		this->options |= NAND_BUSWIDTH_16;
+
+	this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte;
+	this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
+	this->write_word = au_write_word;
+	this->read_word = au_read_word;
+	this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf;
+	this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf;
+	this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf;
+
+#if 1
+        this->select_chip = au_select_chip;
+        this->cmdfunc = au_command;
+#endif
 
 	/* Scan to find existence of the device */
-	if (nand_scan (au1xxx_nand_mtd, 1)) {
-		kfree (au1xxx_nand_mtd);
-		return -ENXIO;
+	if (nand_scan (au1550_mtd, 1)) {
+		retval = -ENXIO;
+		goto outio;
 	}
 
 	/* Register the partitions */
-	add_mtd_partitions(au1xxx_nand_mtd, partition_info, NB_OF(partition_info));
+	add_mtd_partitions(au1550_mtd, partition_info, NB_OF(partition_info));
 
 	return 0;
+
+ outio:
+	iounmap ((void *)p_nand);
+	
+#ifdef CONFIG_MIPS_PB1550
+ outmem:
+#endif
+	kfree (au1550_mtd);
+	return retval;
 }
 
 module_init(au1xxx_nand_init);
@@ -413,24 +722,22 @@
  * Clean up routine
  */
 #ifdef MODULE
-static void __exit au1xxx_nand_cleanup (void)
+static void __exit au1550_cleanup (void)
 {
-	struct nand_chip *this = (struct nand_chip *) &au1xxx_nand_mtd[1];
+	struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
 
-	iounmap ((void *)p_nand);
-
-	/* Unregister partitions */
-	del_mtd_partitions(au1xxx_nand_mtd);
-
-	/* Unregister the device */
-	del_mtd_device (au1xxx_nand_mtd);
+	/* Release resources, unregister device */
+	nand_release (au1550_mtd);
 
 	/* Free the MTD device structure */
-	kfree (au1xxx_nand_mtd);
+	kfree (au1550_mtd);
+
+	/* Unmap */
+	iounmap ((void *)p_nand);
 }
-module_exit(au1xxx_nand_cleanup);
+module_exit(au1550_cleanup);
 #endif
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Embedded Edge, LLC");
-MODULE_DESCRIPTION("NAND flash support for Alchemy");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board");
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/nand_base.c linux-2.4.30/drivers/mtd/nand/nand_base.c
--- linux-2.4.30-orig/drivers/mtd/nand/nand_base.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/nand/nand_base.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,2730 @@
+/*
+ *  drivers/mtd/nand.c
+ *
+ *  Overview:
+ *   This is the generic MTD driver for NAND flash devices. It should be
+ *   capable of working with almost all NAND chips currently available.
+ *   Basic support for AG-AND chips is provided.
+ *   
+ *	Additional technical information is available on
+ *	http://www.linux-mtd.infradead.org/tech/nand.html
+ *	
+ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 		  2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ *  02-08-2004  tglx: support for strange chips, which cannot auto increment 
+ *		pages on read / read_oob
+ *
+ *  03-17-2004  tglx: Check ready before auto increment check. Simon Bayes
+ *		pointed this out, as he marked an auto increment capable chip
+ *		as NOAUTOINCR in the board driver.
+ *		Make reads over block boundaries work too
+ *
+ *  04-14-2004	tglx: first working version for 2k page size chips
+ *  
+ *  05-19-2004  tglx: Basic support for Renesas AG-AND chips
+ *
+ *  09-24-2004  tglx: add support for hardware controllers (e.g. ECC) shared
+ *		among multiple independend devices. Suggestions and initial patch
+ *		from Ben Dooks <ben-mtd@fluff.org>
+ *
+ *  12-05-2004	dmarlin: add workaround for Renesas AG-AND chips "disturb" issue.
+ *		Basically, any block not rewritten may lose data when surrounding blocks
+ *		are rewritten many times.  JFFS2 ensures this doesn't happen for blocks 
+ *		it uses, but the Bad Block Table(s) may not be rewritten.  To ensure they
+ *		do not lose data, force them to be rewritten when some of the surrounding
+ *		blocks are erased.  Rather than tracking a specific nearby block (which 
+ *		could itself go bad), use a page address 'mask' to select several blocks 
+ *		in the same area, and rewrite the BBT when any of them are erased.
+ *
+ *  01-03-2005	dmarlin: added support for the device recovery command sequence for Renesas 
+ *		AG-AND chips.  If there was a sudden loss of power during an erase operation,
+ * 		a "device recovery" operation must be performed when power is restored
+ * 		to ensure correct operation.
+ *
+ *  01-20-2005	dmarlin: added support for optional hardware specific callback routine to 
+ *		perform extra error status checks on erase and write failures.  This required
+ *		adding a wrapper function for nand_read_ecc.
+ *
+ * 08-20-2005	vwool: suspend/resume added
+ *
+ * Credits:
+ *	David Woodhouse for adding multichip support  
+ *	
+ *	Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+ *	rework for 2K page size chips
+ *
+ * TODO:
+ *	Enable cached programming for 2k page size chips
+ *	Check, if mtd->ecctype should be set to MTD_ECC_HW
+ *	if we have HW ecc support.
+ *	The AG-AND chips have nice features for speed improvement,
+ *	which are not supported yet. Read / program 4 pages in one go.
+ *
+ * $Id: nand_base.c,v 1.4 2005/10/25 12:34:20 lel Exp $
+ *
+ * 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/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
+/* Define default oob placement schemes for large and small page devices */
+static struct nand_oobinfo nand_oob_8 = {
+	.useecc = MTD_NANDECC_AUTOPLACE,
+	.eccbytes = 3,
+	.eccpos = {0, 1, 2},
+	.oobfree = { {3, 2}, {6, 2} }
+};
+
+static struct nand_oobinfo nand_oob_16 = {
+	.useecc = MTD_NANDECC_AUTOPLACE,
+	.eccbytes = 6,
+	.eccpos = {0, 1, 2, 3, 6, 7},
+	.oobfree = { {8, 8} }
+};
+
+static struct nand_oobinfo nand_oob_64 = {
+	.useecc = MTD_NANDECC_AUTOPLACE,
+	.eccbytes = 24,
+	.eccpos = {
+		40, 41, 42, 43, 44, 45, 46, 47, 
+		48, 49, 50, 51, 52, 53, 54, 55, 
+		56, 57, 58, 59, 60, 61, 62, 63},
+	.oobfree = { {2, 38} }
+};
+
+/* This is used for padding purposes in nand_write_oob */
+static u_char ffchars[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+/*
+ * NAND low-level MTD interface functions
+ */
+static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
+static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
+static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
+
+static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+			  size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
+static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
+			unsigned long count, loff_t to, size_t * retlen);
+static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs,
+			unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
+static void nand_sync (struct mtd_info *mtd);
+
+/* Some internal functions */
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,
+		struct nand_oobinfo *oobsel, int mode);
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, 
+	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode);
+#else
+#define nand_verify_pages(...) (0)
+#endif
+		
+static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);
+
+/**
+ * nand_release_device - [GENERIC] release chip
+ * @mtd:	MTD device structure
+ * 
+ * Deselect, release chip lock and wake up anyone waiting on the device 
+ */
+static void nand_release_device (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	/* De-select the NAND device */
+	this->select_chip(mtd, -1);
+
+	if (this->controller) {
+		/* Release the controller and the chip */
+		spin_lock(&this->controller->lock);
+		this->controller->active = NULL;
+		this->state = FL_READY;
+		wake_up(&this->controller->wq);
+		spin_unlock(&this->controller->lock);
+	} else {
+		/* Release the chip */
+		spin_lock(&this->chip_lock);
+		this->state = FL_READY;
+		wake_up(&this->wq);
+		spin_unlock(&this->chip_lock);
+	}
+}
+
+/**
+ * nand_read_byte - [DEFAULT] read one byte from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 8bit buswith
+ */
+static u_char nand_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return readb(this->IO_ADDR_R);
+}
+
+/**
+ * nand_write_byte - [DEFAULT] write one byte to the chip
+ * @mtd:	MTD device structure
+ * @byte:	pointer to data byte to write
+ *
+ * Default write function for 8it buswith
+ */
+static void nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	writeb(byte, this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 16bit buswith with 
+ * endianess conversion
+ */
+static u_char nand_read_byte16(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
+}
+
+/**
+ * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip
+ * @mtd:	MTD device structure
+ * @byte:	pointer to data byte to write
+ *
+ * Default write function for 16bit buswith with
+ * endianess conversion
+ */
+static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_word - [DEFAULT] read one word from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 16bit buswith without 
+ * endianess conversion
+ */
+static u16 nand_read_word(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return readw(this->IO_ADDR_R);
+}
+
+/**
+ * nand_write_word - [DEFAULT] write one word to the chip
+ * @mtd:	MTD device structure
+ * @word:	data word to write
+ *
+ * Default write function for 16bit buswith without 
+ * endianess conversion
+ */
+static void nand_write_word(struct mtd_info *mtd, u16 word)
+{
+	struct nand_chip *this = mtd->priv;
+	writew(word, this->IO_ADDR_W);
+}
+
+/**
+ * nand_select_chip - [DEFAULT] control CE line
+ * @mtd:	MTD device structure
+ * @chip:	chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void nand_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	switch(chip) {
+	case -1:
+		this->hwcontrol(mtd, NAND_CTL_CLRNCE);	
+		break;
+	case 0:
+		this->hwcontrol(mtd, NAND_CTL_SETNCE);
+		break;
+
+	default:
+		BUG();
+	}
+}
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ * Default write function for 8bit buswith
+ */
+static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		writeb(buf[i], this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_buf - [DEFAULT] read chip data into buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ * Default read function for 8bit buswith
+ */
+static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		buf[i] = readb(this->IO_ADDR_R);
+}
+
+/**
+ * nand_verify_buf - [DEFAULT] Verify chip data against buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ * Default verify function for 8bit buswith
+ */
+static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		if (buf[i] != readb(this->IO_ADDR_R))
+			return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * nand_write_buf16 - [DEFAULT] write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ * Default write function for 16bit buswith
+ */
+static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+	
+	for (i=0; i<len; i++)
+		writew(p[i], this->IO_ADDR_W);
+		
+}
+
+/**
+ * nand_read_buf16 - [DEFAULT] read chip data into buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ * Default read function for 16bit buswith
+ */
+static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i=0; i<len; i++)
+		p[i] = readw(this->IO_ADDR_R);
+}
+
+/**
+ * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ * Default verify function for 16bit buswith
+ */
+static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i=0; i<len; i++)
+		if (p[i] != readw(this->IO_ADDR_R))
+			return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ * @getchip:	0, if the chip is already selected
+ *
+ * Check, if the block is bad. 
+ */
+static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	int page, chipnr, res = 0;
+	struct nand_chip *this = mtd->priv;
+	u16 bad;
+
+	if (getchip) {
+		page = (int)(ofs >> this->page_shift);
+		chipnr = (int)(ofs >> this->chip_shift);
+
+		/* Grab the lock and see if the device is available */
+		nand_get_device (this, mtd, FL_READING);
+
+		/* Select the NAND device */
+		this->select_chip(mtd, chipnr);
+	} else 
+		page = (int) ofs;	
+
+	if (this->options & NAND_BUSWIDTH_16) {
+		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);
+		bad = cpu_to_le16(this->read_word(mtd));
+		if (this->badblockpos & 0x1)
+			bad >>= 1;
+		if ((bad & 0xFF) != 0xff)
+			res = 1;
+	} else {
+		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);
+		if (this->read_byte(mtd) != 0xff)
+			res = 1;
+	}
+		
+	if (getchip) {
+		/* Deselect and wake up anyone waiting on the device */
+		nand_release_device(mtd);
+	}	
+	
+	return res;
+}
+
+/**
+ * nand_default_block_markbad - [DEFAULT] mark a block bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+*/
+static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *this = mtd->priv;
+	u_char buf[2] = {0, 0};
+	size_t	retlen;
+	int block;
+	
+	/* Get block number */
+	block = ((int) ofs) >> this->bbt_erase_shift;
+	if (this->bbt)
+		this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+	/* Do we have a flash based bad block table ? */
+	if (this->options & NAND_USE_FLASH_BBT)
+		return nand_update_bbt (mtd, ofs);
+		
+	/* We write two bytes, so we dont have to mess with 16 bit access */
+	ofs += mtd->oobsize + (this->badblockpos & ~0x01);
+	return nand_write_oob (mtd, ofs , 2, &retlen, buf);
+}
+
+/** 
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @mtd:	MTD device structure
+ * Check, if the device is write protected 
+ *
+ * The function expects, that the device is already selected 
+ */
+static int nand_check_wp (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	/* Check the WP bit */
+	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+	return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; 
+}
+
+/**
+ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ * @getchip:	0, if the chip is already selected
+ * @allowbbt:	1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+{
+	struct nand_chip *this = mtd->priv;
+	
+	if (!this->bbt)
+		return this->block_bad(mtd, ofs, getchip);
+	
+	/* Return info from the table */
+	return nand_isbad_bbt (mtd, ofs, allowbbt);
+}
+
+/* 
+ * Wait for the ready pin, after a command
+ * The timeout is catched later.
+ */
+static void nand_wait_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	unsigned long	timeo = jiffies + 2;
+
+	/* wait until command is processed or timeout occures */
+	do {
+		if (this->dev_ready(mtd))
+			return;
+	} while (time_before(jiffies, timeo));	
+}
+
+/**
+ * nand_command - [DEFAULT] Send command to NAND device
+ * @mtd:	MTD device structure
+ * @command:	the command to be sent
+ * @column:	the column address for this command, -1 if none
+ * @page_addr:	the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This function is used for small page
+ * devices (256/512 Bytes per page)
+ */
+static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+	register struct nand_chip *this = mtd->priv;
+
+	/* Begin command latch cycle */
+	this->hwcontrol(mtd, NAND_CTL_SETCLE);
+	/*
+	 * Write out the command to the device.
+	 */
+	if (command == NAND_CMD_SEQIN) {
+		int readcmd;
+
+		if (column >= mtd->oobblock) {
+			/* OOB area */
+			column -= mtd->oobblock;
+			readcmd = NAND_CMD_READOOB;
+		} else if (column < 256) {
+			/* First 256 bytes --> READ0 */
+			readcmd = NAND_CMD_READ0;
+		} else {
+			column -= 256;
+			readcmd = NAND_CMD_READ1;
+		}
+		this->write_byte(mtd, readcmd);
+	}
+	this->write_byte(mtd, command);
+
+	/* Set ALE and clear CLE to start address cycle */
+	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+	if (column != -1 || page_addr != -1) {
+		this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+		/* Serially input address */
+		if (column != -1) {
+			/* Adjust columns for 16 bit buswidth */
+			if (this->options & NAND_BUSWIDTH_16)
+				column >>= 1;
+			this->write_byte(mtd, column);
+		}
+		if (page_addr != -1) {
+			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+			/* One more address cycle for devices > 32MiB */
+			if (this->chipsize > (32 << 20))
+				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
+		}
+		/* Latch in address */
+		this->hwcontrol(mtd, NAND_CTL_CLRALE);
+	}
+	
+	/* 
+	 * program and erase have their own busy handlers 
+	 * status and sequential in needs no delay
+	*/
+	switch (command) {
+			
+	case NAND_CMD_PAGEPROG:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_STATUS:
+		return;
+
+	case NAND_CMD_RESET:
+		if (this->dev_ready)	
+			break;
+		udelay(this->chip_delay);
+		this->hwcontrol(mtd, NAND_CTL_SETCLE);
+		this->write_byte(mtd, NAND_CMD_STATUS);
+		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+		while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
+		return;
+
+	/* This applies to read commands */	
+	default:
+		/* 
+		 * If we don't have access to the busy pin, we apply the given
+		 * command delay
+		*/
+		if (!this->dev_ready) {
+			udelay (this->chip_delay);
+			return;
+		}	
+	}
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay (100);
+
+	nand_wait_ready(mtd);
+}
+
+/**
+ * nand_command_lp - [DEFAULT] Send command to NAND large page device
+ * @mtd:	MTD device structure
+ * @command:	the command to be sent
+ * @column:	the column address for this command, -1 if none
+ * @page_addr:	the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This is the version for the new large page devices
+ * We dont have the seperate regions as we have in the small page devices.
+ * We must emulate NAND_CMD_READOOB to keep the code compatible.
+ *
+ */
+static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+	register struct nand_chip *this = mtd->priv;
+
+	/* Emulate NAND_CMD_READOOB */
+	if (command == NAND_CMD_READOOB) {
+		column += mtd->oobblock;
+		command = NAND_CMD_READ0;
+	}
+	
+		
+	/* Begin command latch cycle */
+	this->hwcontrol(mtd, NAND_CTL_SETCLE);
+	/* Write out the command to the device. */
+	this->write_byte(mtd, (command & 0xff));
+	/* End command latch cycle */
+	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+	if (column != -1 || page_addr != -1) {
+		this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+		/* Serially input address */
+		if (column != -1) {
+			/* Adjust columns for 16 bit buswidth */
+			if (this->options & NAND_BUSWIDTH_16)
+				column >>= 1;
+			this->write_byte(mtd, column & 0xff);
+			this->write_byte(mtd, column >> 8);
+		}	
+		if (page_addr != -1) {
+			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+			/* One more address cycle for devices > 128MiB */
+			if (this->chipsize > (128 << 20))
+				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
+		}
+		/* Latch in address */
+		this->hwcontrol(mtd, NAND_CTL_CLRALE);
+	}
+	
+	/* 
+	 * program and erase have their own busy handlers 
+	 * status, sequential in, and deplete1 need no delay
+	 */
+	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_STATUS:
+	case NAND_CMD_DEPLETE1:
+		return;
+
+	/* 
+	 * read error status commands require only a short delay
+	 */
+	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:
+		udelay(this->chip_delay);
+		return;
+
+	case NAND_CMD_RESET:
+		if (this->dev_ready)	
+			break;
+		udelay(this->chip_delay);
+		this->hwcontrol(mtd, NAND_CTL_SETCLE);
+		this->write_byte(mtd, NAND_CMD_STATUS);
+		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+		while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
+		return;
+
+	case NAND_CMD_READ0:
+		/* Begin command latch cycle */
+		this->hwcontrol(mtd, NAND_CTL_SETCLE);
+		/* Write out the start read command */
+		this->write_byte(mtd, NAND_CMD_READSTART);
+		/* End command latch cycle */
+		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+		/* Fall through into ready check */
+		
+	/* This applies to read commands */	
+	default:
+		/* 
+		 * If we don't have access to the busy pin, we apply the given
+		 * command delay
+		*/
+		if (!this->dev_ready) {
+			udelay (this->chip_delay);
+			return;
+		}	
+	}
+
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay (100);
+
+	nand_wait_ready(mtd);
+}
+
+/**
+ * nand_get_device - [GENERIC] Get chip for selected access
+ * @this:	the nand chip descriptor
+ * @mtd:	MTD device structure
+ * @new_state:	the state which is requested 
+ *
+ * Get the device and lock it for exclusive access
+ */
+static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+{
+	struct nand_chip *active;
+	spinlock_t *lock;
+	wait_queue_head_t *wq;
+	DECLARE_WAITQUEUE (wait, current);
+
+	lock = (this->controller) ? &this->controller->lock : &this->chip_lock;
+	wq = (this->controller) ? &this->controller->wq : &this->wq;
+retry:
+	active = this;
+	spin_lock(lock);
+
+	/* Hardware controller shared among independend devices */
+	if (this->controller) {
+		if (this->controller->active)
+			active = this->controller->active;
+		else
+			this->controller->active = this;
+	}
+	if (active == this && this->state == FL_READY) {
+		this->state = new_state;
+		spin_unlock(lock);
+		return 0;
+	}
+	if (new_state == FL_PM_SUSPENDED) {
+		spin_unlock(lock);
+		return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
+	}
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	add_wait_queue(wq, &wait);
+	spin_unlock(lock);
+	schedule();
+	remove_wait_queue(wq, &wait);
+	goto retry;
+}
+
+/**
+ * nand_wait - [DEFAULT]  wait until the command is done
+ * @mtd:	MTD device structure
+ * @this:	NAND chip structure
+ * @state:	state to select the max. timeout value
+ *
+ * Wait for command done. This applies to erase and program only
+ * Erase can take up to 400ms and program up to 20ms according to 
+ * general NAND and SmartMedia specs
+ *
+*/
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+{
+
+	unsigned long	timeo = jiffies;
+	int	status;
+	
+	if (state == FL_ERASING)
+		 timeo += (HZ * 400) / 1000;
+	else
+		 timeo += (HZ * 20) / 1000;
+
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay (100);
+
+	if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
+		this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
+	else	
+		this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+
+	while (time_before(jiffies, timeo)) {		
+		/* Check, if we were interrupted */
+		if (this->state != state)
+			return 0;
+
+		if (this->dev_ready) {
+			if (this->dev_ready(mtd))
+				break;	
+		} else {
+			if (this->read_byte(mtd) & NAND_STATUS_READY)
+				break;
+		}
+		cond_resched();
+	}
+	status = (int) this->read_byte(mtd);
+	return status;
+}
+
+/**
+ * nand_write_page - [GENERIC] write one page
+ * @mtd:	MTD device structure
+ * @this:	NAND chip structure
+ * @page: 	startpage inside the chip, must be called with (page & this->pagemask)
+ * @oob_buf:	out of band data buffer
+ * @oobsel:	out of band selecttion structre
+ * @cached:	1 = enable cached programming if supported by chip
+ *
+ * Nand_page_program function is used for write and writev !
+ * This function will always program a full page of data
+ * If you call it with a non page aligned buffer, you're lost :)
+ *
+ * Cached programming is not supported yet.
+ */
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, 
+	u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached)
+{
+	int 	i, status;
+	u_char	ecc_code[32];
+	int	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
+	int  	*oob_config = oobsel->eccpos;
+	int	datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
+	int	eccbytes = 0;
+	
+	/* FIXME: Enable cached programming */
+	cached = 0;
+	
+	/* Send command to begin auto page programming */
+	this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
+
+	/* Write out complete page of data, take care of eccmode */
+	switch (eccmode) {
+	/* No ecc, write all */
+	case NAND_ECC_NONE:
+/*		printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); */
+		this->write_buf(mtd, this->data_poi, mtd->oobblock);
+		break;
+		
+	/* Software ecc 3/256, write all */
+	case NAND_ECC_SOFT:
+		for (; eccsteps; eccsteps--) {
+			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
+			for (i = 0; i < 3; i++, eccidx++)
+				oob_buf[oob_config[eccidx]] = ecc_code[i];
+			datidx += this->eccsize;
+		}
+		this->write_buf(mtd, this->data_poi, mtd->oobblock);
+		break;
+	default:
+		eccbytes = this->eccbytes;
+		for (; eccsteps; eccsteps--) {
+			/* enable hardware ecc logic for write */
+			this->enable_hwecc(mtd, NAND_ECC_WRITE);
+			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
+			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
+			for (i = 0; i < eccbytes; i++, eccidx++)
+				oob_buf[oob_config[eccidx]] = ecc_code[i];
+			/* If the hardware ecc provides syndromes then
+			 * the ecc code must be written immidiately after
+			 * the data bytes (words) */
+			if (this->options & NAND_HWECC_SYNDROME)
+				this->write_buf(mtd, ecc_code, eccbytes);
+			datidx += this->eccsize;
+		}
+		break;
+	}
+										
+	/* Write out OOB data */
+	if (this->options & NAND_HWECC_SYNDROME)
+		this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
+	else 
+		this->write_buf(mtd, oob_buf, mtd->oobsize);
+
+	/* Send command to actually program the data */
+	this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
+
+	if (!cached) {
+		/* call wait ready function */
+		status = this->waitfunc (mtd, this, FL_WRITING);
+
+		/* See if operation failed and additional status checks are available */
+		if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
+			status = this->errstat(mtd, this, FL_WRITING, status, page);
+		}
+
+		/* See if device thinks it succeeded */
+		if (status & NAND_STATUS_FAIL) {
+			DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
+			return -EIO;
+		}
+	} else {
+		/* FIXME: Implement cached programming ! */
+		/* wait until cache is ready*/
+		// status = this->waitfunc (mtd, this, FL_CACHEDRPG);
+	}
+	return 0;	
+}
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+/**
+ * nand_verify_pages - [GENERIC] verify the chip contents after a write
+ * @mtd:	MTD device structure
+ * @this:	NAND chip structure
+ * @page: 	startpage inside the chip, must be called with (page & this->pagemask)
+ * @numpages:	number of pages to verify
+ * @oob_buf:	out of band data buffer
+ * @oobsel:	out of band selecttion structre
+ * @chipnr:	number of the current chip
+ * @oobmode:	1 = full buffer verify, 0 = ecc only
+ *
+ * The NAND device assumes that it is always writing to a cleanly erased page.
+ * Hence, it performs its internal write verification only on bits that 
+ * transitioned from 1 to 0. The device does NOT verify the whole page on a
+ * byte by byte basis. It is possible that the page was not completely erased 
+ * or the page is becoming unusable due to wear. The read with ECC would catch 
+ * the error later when the ECC page check fails, but we would rather catch 
+ * it early in the page write stage. Better to write no data than invalid data.
+ */
+static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, 
+	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
+{
+	int 	i, j, datidx = 0, oobofs = 0, res = -EIO;
+	int	eccsteps = this->eccsteps;
+	int	hweccbytes; 
+	u_char 	oobdata[64];
+
+	hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
+
+	/* Send command to read back the first page */
+	this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
+
+	for(;;) {
+		for (j = 0; j < eccsteps; j++) {
+			/* Loop through and verify the data */
+			if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) {
+				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+				goto out;
+			}
+			datidx += mtd->eccsize;
+			/* Have we a hw generator layout ? */
+			if (!hweccbytes)
+				continue;
+			if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) {
+				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+				goto out;
+			}
+			oobofs += hweccbytes;
+		}
+
+		/* check, if we must compare all data or if we just have to
+		 * compare the ecc bytes
+		 */
+		if (oobmode) {
+			if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
+				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+				goto out;
+			}
+		} else {
+			/* Read always, else autoincrement fails */
+			this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
+
+			if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
+				int ecccnt = oobsel->eccbytes;
+		
+				for (i = 0; i < ecccnt; i++) {
+					int idx = oobsel->eccpos[i];
+					if (oobdata[idx] != oob_buf[oobofs + idx] ) {
+						DEBUG (MTD_DEBUG_LEVEL0,
+					       	"%s: Failed ECC write "
+						"verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
+						goto out;
+					}
+				}
+			}	
+		}
+		oobofs += mtd->oobsize - hweccbytes * eccsteps;
+		page++;
+		numpages--;
+
+		/* Apply delay or wait for ready/busy pin 
+		 * Do this before the AUTOINCR check, so no problems
+		 * arise if a chip which does auto increment
+		 * is marked as NOAUTOINCR by the board driver.
+		 * Do this also before returning, so the chip is
+		 * ready for the next command.
+		*/
+		if (!this->dev_ready) 
+			udelay (this->chip_delay);
+		else
+			nand_wait_ready(mtd);
+
+		/* All done, return happy */
+		if (!numpages)
+			return 0;
+		
+			
+		/* Check, if the chip supports auto page increment */ 
+		if (!NAND_CANAUTOINCR(this))
+			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+	}
+	/* 
+	 * Terminate the read command. We come here in case of an error
+	 * So we must issue a reset command.
+	 */
+out:	 
+	this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1);
+	return res;
+}
+#endif
+
+/**
+ * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ *
+ * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL
+ * and flags = 0xff
+ */
+static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+{
+	return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff);
+}
+
+
+/**
+ * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ * @oob_buf:	filesystem supplied oob data buffer
+ * @oobsel:	oob selection structure
+ *
+ * This function simply calls nand_do_read_ecc with flags = 0xff
+ */
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+			  size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
+{
+	/* use userspace supplied oobinfo, if zero */
+	if (oobsel == NULL)
+		oobsel = &mtd->oobinfo;
+	return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff);
+}
+
+
+/**
+ * nand_do_read_ecc - [MTD Interface] Read data with ECC
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ * @oob_buf:	filesystem supplied oob data buffer (can be NULL)
+ * @oobsel:	oob selection structure
+ * @flags:	flag to indicate if nand_get_device/nand_release_device should be preformed
+ *		and how many corrected error bits are acceptable:
+ *		  bits 0..7 - number of tolerable errors
+ *		  bit  8    - 0 == do not get/release chip, 1 == get/release chip
+ *
+ * NAND read with ECC
+ */
+int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+			     size_t * retlen, u_char * buf, u_char * oob_buf, 
+			     struct nand_oobinfo *oobsel, int flags)
+{
+
+	int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
+	int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
+	struct nand_chip *this = mtd->priv;
+	u_char *data_poi, *oob_data = oob_buf;
+	u_char ecc_calc[32];
+	u_char ecc_code[32];
+        int eccmode, eccsteps;
+	int	*oob_config, datidx;
+	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+	int	eccbytes;
+	int	compareecc = 1;
+	int	oobreadlen;
+
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
+		*retlen = 0;
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	if (flags & NAND_GET_DEVICE)
+		nand_get_device (this, mtd, FL_READING);
+
+	/* Autoplace of oob data ? Use the default placement scheme */
+	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
+		oobsel = this->autooob;
+		
+	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
+	oob_config = oobsel->eccpos;
+
+	/* Select the NAND device */
+	chipnr = (int)(from >> this->chip_shift);
+	this->select_chip(mtd, chipnr);
+
+	/* First we calculate the starting page */
+	realpage = (int) (from >> this->page_shift);
+	page = realpage & this->pagemask;
+
+	/* Get raw starting column */
+	col = from & (mtd->oobblock - 1);
+
+	end = mtd->oobblock;
+	ecc = this->eccsize;
+	eccbytes = this->eccbytes;
+	
+	if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
+		compareecc = 0;
+
+	oobreadlen = mtd->oobsize;
+	if (this->options & NAND_HWECC_SYNDROME) 
+		oobreadlen -= oobsel->eccbytes;
+
+	/* Loop until all data read */
+	while (read < len) {
+		
+		int aligned = (!col && (len - read) >= end);
+		/* 
+		 * If the read is not page aligned, we have to read into data buffer
+		 * due to ecc, else we read into return buffer direct
+		 */
+		if (aligned)
+			data_poi = &buf[read];
+		else 
+			data_poi = this->data_buf;
+		
+		/* Check, if we have this page in the buffer 
+		 *
+		 * FIXME: Make it work when we must provide oob data too,
+		 * check the usage of data_buf oob field
+		 */
+		if (realpage == this->pagebuf && !oob_buf) {
+			/* aligned read ? */
+			if (aligned)
+				memcpy (data_poi, this->data_buf, end);
+			goto readdata;
+		}
+
+		/* Check, if we must send the read command */
+		if (sndcmd) {
+			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+			sndcmd = 0;
+		}	
+
+		/* get oob area, if we have no oob buffer from fs-driver */
+		if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
+			oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+			oob_data = &this->data_buf[end];
+
+		eccsteps = this->eccsteps;
+		
+		switch (eccmode) {
+		case NAND_ECC_NONE: {	/* No ECC, Read in a page */
+			static unsigned long lastwhinge = 0;
+			if ((lastwhinge / HZ) != (jiffies / HZ)) {
+/*				printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); */
+				lastwhinge = jiffies;
+			}
+			this->read_buf(mtd, data_poi, end);
+			break;
+		}
+			
+		case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */
+			this->read_buf(mtd, data_poi, end);
+			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) 
+				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+			break;	
+
+		default:
+			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
+				this->enable_hwecc(mtd, NAND_ECC_READ);
+				this->read_buf(mtd, &data_poi[datidx], ecc);
+
+				/* HW ecc with syndrome calculation must read the
+				 * syndrome from flash immidiately after the data */
+				if (!compareecc) {
+					/* Some hw ecc generators need to know when the
+					 * syndrome is read from flash */
+					this->enable_hwecc(mtd, NAND_ECC_READSYN);
+					this->read_buf(mtd, &oob_data[i], eccbytes);
+					/* We calc error correction directly, it checks the hw
+					 * generator for an error, reads back the syndrome and
+					 * does the error correction on the fly */
+					ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]);
+					if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
+						DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " 
+							"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
+						ecc_failed++;
+					}
+				} else {
+					this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+				}	
+			}
+			break;						
+		}
+
+		/* read oobdata */
+		this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
+
+		/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
+		if (!compareecc)
+			goto readoob;	
+		
+		/* Pick the ECC bytes out of the oob data */
+		for (j = 0; j < oobsel->eccbytes; j++)
+			ecc_code[j] = oob_data[oob_config[j]];
+
+		/* correct data, if neccecary */
+		for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
+			ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
+			
+			/* Get next chunk of ecc bytes */
+			j += eccbytes;
+			
+			/* Check, if we have a fs supplied oob-buffer, 
+			 * This is the legacy mode. Used by YAFFS1
+			 * Should go away some day
+			 */
+			if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { 
+				int *p = (int *)(&oob_data[mtd->oobsize]);
+				p[i] = ecc_status;
+			}
+			
+			if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {	
+				DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
+				ecc_failed++;
+			}
+		}		
+
+	readoob:
+		/* check, if we have a fs supplied oob-buffer */
+		if (oob_buf) {
+			/* without autoplace. Legacy mode used by YAFFS1 */
+			switch(oobsel->useecc) {
+			case MTD_NANDECC_AUTOPLACE:
+			case MTD_NANDECC_AUTOPL_USR:
+				/* Walk through the autoplace chunks */
+				for (i = 0; oobsel->oobfree[i][1]; i++) {
+					int from = oobsel->oobfree[i][0];
+					int num = oobsel->oobfree[i][1];
+					memcpy(&oob_buf[oob], &oob_data[from], num);
+					oob += num;
+				}
+				break;
+			case MTD_NANDECC_PLACE:
+				/* YAFFS1 legacy mode */
+				oob_data += this->eccsteps * sizeof (int);
+			default:
+				oob_data += mtd->oobsize;
+			}
+		}
+	readdata:
+		/* Partial page read, transfer data into fs buffer */
+		if (!aligned) { 
+			for (j = col; j < end && read < len; j++)
+				buf[read++] = data_poi[j];
+			this->pagebuf = realpage;	
+		} else		
+			read += mtd->oobblock;
+
+		/* Apply delay or wait for ready/busy pin 
+		 * Do this before the AUTOINCR check, so no problems
+		 * arise if a chip which does auto increment
+		 * is marked as NOAUTOINCR by the board driver.
+		*/
+		if (!this->dev_ready) 
+			udelay (this->chip_delay);
+		else
+			nand_wait_ready(mtd);
+			
+		if (read == len)
+			break;	
+
+		/* For subsequent reads align to page boundary. */
+		col = 0;
+		/* Increment page address */
+		realpage++;
+
+		page = realpage & this->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!page) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+		}
+		/* Check, if the chip supports auto page increment 
+		 * or if we have hit a block boundary. 
+		*/ 
+		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+			sndcmd = 1;				
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	if (flags & NAND_GET_DEVICE)
+		nand_release_device(mtd);
+
+	/*
+	 * Return success, if no ECC failures, else -EBADMSG
+	 * fs driver will take care of that, because
+	 * retlen == desired len and result == -EBADMSG
+	 */
+	*retlen = read;
+	return ecc_failed ? -EBADMSG : 0;
+}
+
+/**
+ * nand_read_oob - [MTD Interface] NAND read out-of-band
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ *
+ * NAND read out-of-band data from the spare area
+ */
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+{
+	int i, col, page, chipnr;
+	struct nand_chip *this = mtd->priv;
+	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+	/* Shift to get page */
+	page = (int)(from >> this->page_shift);
+	chipnr = (int)(from >> this->chip_shift);
+	
+	/* Mask to get column */
+	col = from & (mtd->oobsize - 1);
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
+		*retlen = 0;
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd , FL_READING);
+
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Send the read command */
+	this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
+	/* 
+	 * Read the data, if we read more than one page
+	 * oob data, let the device transfer the data !
+	 */
+	i = 0;
+	while (i < len) {
+		int thislen = mtd->oobsize - col;
+		thislen = min_t(int, thislen, len);
+		this->read_buf(mtd, &buf[i], thislen);
+		i += thislen;
+
+		/* Read more ? */
+		if (i < len) {
+			page++;
+			col = 0;
+
+			/* Check, if we cross a chip boundary */
+			if (!(page & this->pagemask)) {
+				chipnr++;
+				this->select_chip(mtd, -1);
+				this->select_chip(mtd, chipnr);
+			}
+				
+			/* Apply delay or wait for ready/busy pin 
+			 * Do this before the AUTOINCR check, so no problems
+			 * arise if a chip which does auto increment
+			 * is marked as NOAUTOINCR by the board driver.
+			 */
+			if (!this->dev_ready) 
+				udelay (this->chip_delay);
+			else
+				nand_wait_ready(mtd);
+
+			/* Check, if the chip supports auto page increment 
+			 * or if we have hit a block boundary. 
+			*/ 
+			if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
+				/* For subsequent page reads set offset to 0 */
+			        this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
+			}
+		}
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+
+	/* Return happy */
+	*retlen = len;
+	return 0;
+}
+
+/**
+ * nand_read_raw - [GENERIC] Read raw data including oob into buffer
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @ooblen:	number of oob data bytes to read
+ *
+ * Read raw data including oob into buffer
+ */
+int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen)
+{
+	struct nand_chip *this = mtd->priv;
+	int page = (int) (from >> this->page_shift);
+	int chip = (int) (from >> this->chip_shift);
+	int sndcmd = 1;
+	int cnt = 0;
+	int pagesize = mtd->oobblock + mtd->oobsize;
+	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd , FL_READING);
+
+	this->select_chip (mtd, chip);
+	
+	/* Add requested oob length */
+	len += ooblen;
+	
+	while (len) {
+		if (sndcmd)
+			this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
+		sndcmd = 0;	
+
+		this->read_buf (mtd, &buf[cnt], pagesize);
+
+		len -= pagesize;
+		cnt += pagesize;
+		page++;
+		
+		if (!this->dev_ready) 
+			udelay (this->chip_delay);
+		else
+			nand_wait_ready(mtd);
+			
+		/* Check, if the chip supports auto page increment */ 
+		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+			sndcmd = 1;
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+	return 0;
+}
+
+
+/** 
+ * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer 
+ * @mtd:	MTD device structure
+ * @fsbuf:	buffer given by fs driver
+ * @oobsel:	out of band selection structre
+ * @autoplace:	1 = place given buffer into the oob bytes
+ * @numpages:	number of pages to prepare
+ *
+ * Return:
+ * 1. Filesystem buffer available and autoplacement is off,
+ *    return filesystem buffer
+ * 2. No filesystem buffer or autoplace is off, return internal
+ *    buffer
+ * 3. Filesystem buffer is given and autoplace selected
+ *    put data from fs buffer into internal buffer and
+ *    retrun internal buffer
+ *
+ * Note: The internal buffer is filled with 0xff. This must
+ * be done only once, when no autoplacement happens
+ * Autoplacement sets the buffer dirty flag, which
+ * forces the 0xff fill before using the buffer again.
+ *
+*/
+static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel,
+		int autoplace, int numpages)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, len, ofs;
+
+	/* Zero copy fs supplied buffer */
+	if (fsbuf && !autoplace) 
+		return fsbuf;
+
+	/* Check, if the buffer must be filled with ff again */
+	if (this->oobdirty) {	
+		memset (this->oob_buf, 0xff, 
+			mtd->oobsize << (this->phys_erase_shift - this->page_shift));
+		this->oobdirty = 0;
+	}	
+	
+	/* If we have no autoplacement or no fs buffer use the internal one */
+	if (!autoplace || !fsbuf)
+		return this->oob_buf;
+	
+	/* Walk through the pages and place the data */
+	this->oobdirty = 1;
+	ofs = 0;
+	while (numpages--) {
+		for (i = 0, len = 0; len < mtd->oobavail; i++) {
+			int to = ofs + oobsel->oobfree[i][0];
+			int num = oobsel->oobfree[i][1];
+			memcpy (&this->oob_buf[to], fsbuf, num);
+			len += num;
+			fsbuf += num;
+		}
+		ofs += mtd->oobavail;
+	}
+	return this->oob_buf;
+}
+
+#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
+
+/**
+ * nand_write - [MTD Interface] compability function for nand_write_ecc
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @len:	number of bytes to write
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
+ *
+ * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL
+ *
+*/
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+{
+	return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
+}
+			   
+/**
+ * nand_write_ecc - [MTD Interface] NAND write with ECC
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @len:	number of bytes to write
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
+ * @eccbuf:	filesystem supplied oob data buffer
+ * @oobsel:	oob selection structure
+ *
+ * NAND write with ECC
+ */
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
+{
+	int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr;
+	int autoplace = 0, numpages, totalpages;
+	struct nand_chip *this = mtd->priv;
+	u_char *oobbuf, *bufstart;
+	int	ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+	/* Initialize retlen, in case of early exit */
+	*retlen = 0;
+
+	/* Do not allow write past end of device */
+	if ((to + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
+		return -EINVAL;
+	}
+
+	/* reject writes, which are not page aligned */	
+	if (NOTALIGNED (to) || NOTALIGNED(len)) {
+		printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd, FL_WRITING);
+
+	/* Calculate chipnr */
+	chipnr = (int)(to >> this->chip_shift);
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd))
+		goto out;
+
+	/* if oobsel is NULL, use chip defaults */
+	if (oobsel == NULL) 
+		oobsel = &mtd->oobinfo;		
+		
+	/* Autoplace of oob data ? Use the default placement scheme */
+	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
+		oobsel = this->autooob;
+		autoplace = 1;
+	}	
+	if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+		autoplace = 1;
+
+	/* Setup variables and oob buffer */
+	totalpages = len >> this->page_shift;
+	page = (int) (to >> this->page_shift);
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page <= this->pagebuf && this->pagebuf < (page + totalpages))  
+		this->pagebuf = -1;
+	
+	/* Set it relative to chip */
+	page &= this->pagemask;
+	startpage = page;
+	/* Calc number of pages we can write in one go */
+	numpages = min (ppblock - (startpage  & (ppblock - 1)), totalpages);
+	oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
+	bufstart = (u_char *)buf;
+
+	/* Loop until all data is written */
+	while (written < len) {
+
+		this->data_poi = (u_char*) &buf[written];
+		/* Write one page. If this is the last page to write
+		 * or the last page in this block, then use the
+		 * real pageprogram command, else select cached programming
+		 * if supported by the chip.
+		 */
+		ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));
+		if (ret) {
+			DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret);
+			goto out;
+		}	
+		/* Next oob page */
+		oob += mtd->oobsize;
+		/* Update written bytes count */
+		written += mtd->oobblock;
+		if (written == len) 
+			goto cmp;
+		
+		/* Increment page address */
+		page++;
+
+		/* Have we hit a block boundary ? Then we have to verify and
+		 * if verify is ok, we have to setup the oob buffer for
+		 * the next pages.
+		*/
+		if (!(page & (ppblock - 1))){
+			int ofs;
+			this->data_poi = bufstart;
+			ret = nand_verify_pages (mtd, this, startpage, 
+				page - startpage,
+				oobbuf, oobsel, chipnr, (eccbuf != NULL));
+			if (ret) {
+				DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
+				goto out;
+			}	
+			*retlen = written;
+
+			ofs = autoplace ? mtd->oobavail : mtd->oobsize;
+			if (eccbuf)
+				eccbuf += (page - startpage) * ofs;
+			totalpages -= page - startpage;
+			numpages = min (totalpages, ppblock);
+			page &= this->pagemask;
+			startpage = page;
+			oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, 
+					autoplace, numpages);
+			/* Check, if we cross a chip boundary */
+			if (!page) {
+				chipnr++;
+				this->select_chip(mtd, -1);
+				this->select_chip(mtd, chipnr);
+			}
+		}
+	}
+	/* Verify the remaining pages */
+cmp:
+	this->data_poi = bufstart;
+ 	ret = nand_verify_pages (mtd, this, startpage, totalpages,
+		oobbuf, oobsel, chipnr, (eccbuf != NULL));
+	if (!ret)
+		*retlen = written;
+	else	
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
+
+out:
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+
+	return ret;
+}
+
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write out-of-band
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @len:	number of bytes to write
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
+ *
+ * NAND write out-of-band
+ */
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+{
+	int column, page, status, ret = -EIO, chipnr;
+	struct nand_chip *this = mtd->priv;
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+	/* Shift to get page */
+	page = (int) (to >> this->page_shift);
+	chipnr = (int) (to >> this->chip_shift);
+
+	/* Mask to get column */
+	column = to & (mtd->oobsize - 1);
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Do not allow write past end of page */
+	if ((column + len) > mtd->oobsize) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd, FL_WRITING);
+
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Reset the chip. Some chips (like the Toshiba TC5832DC found
+	   in one of my DiskOnChip 2000 test units) will clear the whole
+	   data page too if we don't do this. I have no clue why, but
+	   I seem to have 'fixed' it in the doc2000 driver in
+	   August 1999.  dwmw2. */
+	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd))
+		goto out;
+	
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page == this->pagebuf)
+		this->pagebuf = -1;
+
+	if (NAND_MUST_PAD(this)) {
+		/* Write out desired data */
+		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
+		/* prepad 0xff for partial programming */
+		this->write_buf(mtd, ffchars, column);
+		/* write data */
+		this->write_buf(mtd, buf, len);
+		/* postpad 0xff for partial programming */
+		this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
+	} else {
+		/* Write out desired data */
+		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
+		/* write data */
+		this->write_buf(mtd, buf, len);
+	}
+	/* Send command to program the OOB data */
+	this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+	status = this->waitfunc (mtd, this, FL_WRITING);
+
+	/* See if device thinks it succeeded */
+	if (status & NAND_STATUS_FAIL) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
+		ret = -EIO;
+		goto out;
+	}
+	/* Return happy */
+	*retlen = len;
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+	/* Send command to read back the data */
+	this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
+
+	if (this->verify_buf(mtd, buf, len)) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
+		ret = -EIO;
+		goto out;
+	}
+#endif
+	ret = 0;
+out:
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+
+	return ret;
+}
+
+
+/**
+ * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc
+ * @mtd:	MTD device structure
+ * @vecs:	the iovectors to write
+ * @count:	number of vectors
+ * @to:		offset to write to
+ * @retlen:	pointer to variable to store the number of written bytes
+ *
+ * NAND write with kvec. This just calls the ecc function
+ */
+static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, 
+		loff_t to, size_t * retlen)
+{
+	return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL));	
+}
+
+/**
+ * nand_writev_ecc - [MTD Interface] write with iovec with ecc
+ * @mtd:	MTD device structure
+ * @vecs:	the iovectors to write
+ * @count:	number of vectors
+ * @to:		offset to write to
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @eccbuf:	filesystem supplied oob data buffer
+ * @oobsel:	oob selection structure
+ *
+ * NAND write with iovec with ecc
+ */
+static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, 
+		loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
+{
+	int i, page, len, total_len, ret = -EIO, written = 0, chipnr;
+	int oob, numpages, autoplace = 0, startpage;
+	struct nand_chip *this = mtd->priv;
+	int	ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+	u_char *oobbuf, *bufstart;
+
+	/* Preset written len for early exit */
+	*retlen = 0;
+
+	/* Calculate total length of data */
+	total_len = 0;
+	for (i = 0; i < count; i++)
+		total_len += (int) vecs[i].iov_len;
+
+	DEBUG (MTD_DEBUG_LEVEL3,
+	       "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
+
+	/* Do not allow write past end of page */
+	if ((to + total_len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
+		return -EINVAL;
+	}
+
+	/* reject writes, which are not page aligned */	
+	if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
+		printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd, FL_WRITING);
+
+	/* Get the current chip-nr */
+	chipnr = (int) (to >> this->chip_shift);
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd))
+		goto out;
+
+	/* if oobsel is NULL, use chip defaults */
+	if (oobsel == NULL) 
+		oobsel = &mtd->oobinfo;		
+
+	/* Autoplace of oob data ? Use the default placement scheme */
+	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
+		oobsel = this->autooob;
+		autoplace = 1;
+	}	
+	if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
+		autoplace = 1;
+
+	/* Setup start page */
+	page = (int) (to >> this->page_shift);
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift))  
+		this->pagebuf = -1;
+
+	startpage = page & this->pagemask;
+
+	/* Loop until all kvec' data has been written */
+	len = 0;
+	while (count) {
+		/* If the given tuple is >= pagesize then
+		 * write it out from the iov
+		 */
+		if ((vecs->iov_len - len) >= mtd->oobblock) {
+			/* Calc number of pages we can write
+			 * out of this iov in one go */
+			numpages = (vecs->iov_len - len) >> this->page_shift;
+			/* Do not cross block boundaries */
+			numpages = min (ppblock - (startpage & (ppblock - 1)), numpages);
+			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+			bufstart = (u_char *)vecs->iov_base;
+			bufstart += len;
+			this->data_poi = bufstart;
+			oob = 0;
+			for (i = 1; i <= numpages; i++) {
+				/* Write one page. If this is the last page to write
+				 * then use the real pageprogram command, else select 
+				 * cached programming if supported by the chip.
+				 */
+				ret = nand_write_page (mtd, this, page & this->pagemask, 
+					&oobbuf[oob], oobsel, i != numpages);
+				if (ret)
+					goto out;
+				this->data_poi += mtd->oobblock;
+				len += mtd->oobblock;
+				oob += mtd->oobsize;
+				page++;
+			}
+			/* Check, if we have to switch to the next tuple */
+			if (len >= (int) vecs->iov_len) {
+				vecs++;
+				len = 0;
+				count--;
+			}
+		} else {
+			/* We must use the internal buffer, read data out of each 
+			 * tuple until we have a full page to write
+			 */
+			int cnt = 0;
+			while (cnt < mtd->oobblock) {
+				if (vecs->iov_base != NULL && vecs->iov_len) 
+					this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
+				/* Check, if we have to switch to the next tuple */
+				if (len >= (int) vecs->iov_len) {
+					vecs++;
+					len = 0;
+					count--;
+				}
+			}
+			this->pagebuf = page;	
+			this->data_poi = this->data_buf;	
+			bufstart = this->data_poi;
+			numpages = 1;		
+			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+			ret = nand_write_page (mtd, this, page & this->pagemask,
+				oobbuf, oobsel, 0);
+			if (ret)
+				goto out;
+			page++;
+		}
+
+		this->data_poi = bufstart;
+		ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);
+		if (ret)
+			goto out;
+			
+		written += mtd->oobblock * numpages;
+		/* All done ? */
+		if (!count)
+			break;
+
+		startpage = page & this->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!startpage) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+		}
+	}
+	ret = 0;
+out:
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+
+	*retlen = written;
+	return ret;
+}
+
+/**
+ * single_erease_cmd - [GENERIC] NAND standard block erase command function
+ * @mtd:	MTD device structure
+ * @page:	the page address of the block which will be erased
+ *
+ * Standard erase command for NAND chips
+ */
+static void single_erase_cmd (struct mtd_info *mtd, int page)
+{
+	struct nand_chip *this = mtd->priv;
+	/* Send commands to erase a block */
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * multi_erease_cmd - [GENERIC] AND specific block erase command function
+ * @mtd:	MTD device structure
+ * @page:	the page address of the block which will be erased
+ *
+ * AND multi block erase command function
+ * Erase 4 consecutive blocks
+ */
+static void multi_erase_cmd (struct mtd_info *mtd, int page)
+{
+	struct nand_chip *this = mtd->priv;
+	/* Send commands to erase a block */
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd:	MTD device structure
+ * @instr:	erase instruction
+ *
+ * Erase one ore more blocks
+ */
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+	return nand_erase_nand (mtd, instr, 0);
+}
+ 
+#define BBT_PAGE_MASK	0xffffff3f
+/**
+ * nand_erase_intern - [NAND Interface] erase block(s)
+ * @mtd:	MTD device structure
+ * @instr:	erase instruction
+ * @allowbbt:	allow erasing the bbt area
+ *
+ * Erase one ore more blocks
+ */
+int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
+{
+	int page, len, status, pages_per_block, ret, chipnr;
+	struct nand_chip *this = mtd->priv;
+	int rewrite_bbt[NAND_MAX_CHIPS]={0};	/* flags to indicate the page, if bbt needs to be rewritten. */
+	unsigned int bbt_masked_page;		/* bbt mask to compare to page being erased. */
+						/* It is used to see if the current page is in the same */
+						/*   256 block group and the same bank as the bbt. */
+
+	DEBUG (MTD_DEBUG_LEVEL3,
+	       "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
+
+	/* Start address must align on block boundary */
+	if (instr->addr & ((1 << this->phys_erase_shift) - 1)) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+		return -EINVAL;
+	}
+
+	/* Length must align on block boundary */
+	if (instr->len & ((1 << this->phys_erase_shift) - 1)) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
+		return -EINVAL;
+	}
+
+	/* Do not allow erase past end of device */
+	if ((instr->len + instr->addr) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
+		return -EINVAL;
+	}
+
+	instr->fail_addr = 0xffffffff;
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd, FL_ERASING);
+
+	/* Shift to get first page */
+	page = (int) (instr->addr >> this->page_shift);
+	chipnr = (int) (instr->addr >> this->chip_shift);
+
+	/* Calculate pages in each block */
+	pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
+
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Check the WP bit */
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd)) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
+		instr->state = MTD_ERASE_FAILED;
+		goto erase_exit;
+	}
+
+	/* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */
+	if (this->options & BBT_AUTO_REFRESH) {
+		bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
+	} else {
+		bbt_masked_page = 0xffffffff;	/* should not match anything */
+	}
+
+	/* Loop through the pages */
+	len = instr->len;
+
+	instr->state = MTD_ERASING;
+
+	while (len) {
+		/* Check if we have a bad block, we do not erase bad blocks ! */
+		if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
+			printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
+			instr->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}
+		
+		/* Invalidate the page cache, if we erase the block which contains 
+		   the current cached page */
+		if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
+			this->pagebuf = -1;
+
+		this->erase_cmd (mtd, page & this->pagemask);
+		
+		status = this->waitfunc (mtd, this, FL_ERASING);
+
+		/* See if operation failed and additional status checks are available */
+		if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
+			status = this->errstat(mtd, this, FL_ERASING, status, page);
+		}
+
+		/* See if block erase succeeded */
+		if (status & NAND_STATUS_FAIL) {
+			DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
+			instr->state = MTD_ERASE_FAILED;
+			instr->fail_addr = (page << this->page_shift);
+			goto erase_exit;
+		}
+
+		/* if BBT requires refresh, set the BBT rewrite flag to the page being erased */
+		if (this->options & BBT_AUTO_REFRESH) {
+			if (((page & BBT_PAGE_MASK) == bbt_masked_page) && 
+			     (page != this->bbt_td->pages[chipnr])) {
+				rewrite_bbt[chipnr] = (page << this->page_shift);
+			}
+		}
+		
+		/* Increment page address and decrement length */
+		len -= (1 << this->phys_erase_shift);
+		page += pages_per_block;
+
+		/* Check, if we cross a chip boundary */
+		if (len && !(page & this->pagemask)) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+
+			/* if BBT requires refresh and BBT-PERCHIP, 
+			 *   set the BBT page mask to see if this BBT should be rewritten */
+			if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) {
+				bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
+			}
+
+		}
+	}
+	instr->state = MTD_ERASE_DONE;
+
+erase_exit:
+
+	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+	/* Do call back function */
+	if (!ret)
+		mtd_erase_callback(instr);
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+
+	/* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */
+	if ((this->options & BBT_AUTO_REFRESH) && (!ret)) {
+		for (chipnr = 0; chipnr < this->numchips; chipnr++) {
+			if (rewrite_bbt[chipnr]) {
+				/* update the BBT for chip */
+				DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", 
+					chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]);
+				nand_update_bbt (mtd, rewrite_bbt[chipnr]);
+			}
+		}
+	}
+
+	/* Return more or less happy */
+	return ret;
+}
+
+/**
+ * nand_sync - [MTD Interface] sync
+ * @mtd:	MTD device structure
+ *
+ * Sync is actually a wait for chip ready function
+ */
+static void nand_sync (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device (this, mtd, FL_SYNCING);
+	/* Release it and go back */
+	nand_release_device (mtd);
+}
+
+
+/**
+ * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset relative to mtd start
+ */
+static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
+{
+	/* Check for invalid offset */
+	if (ofs > mtd->size) 
+		return -EINVAL;
+	
+	return nand_block_checkbad (mtd, ofs, 1, 0);
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset relative to mtd start
+ */
+static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *this = mtd->priv;
+	int ret;
+
+        if ((ret = nand_block_isbad(mtd, ofs))) {
+        	/* If it was bad already, return success and do nothing. */
+		if (ret > 0)
+			return 0;
+        	return ret;
+        }
+
+	return this->block_markbad(mtd, ofs);
+}
+
+/**
+ * nand_suspend - [MTD Interface] Suspend the NAND flash
+ * @mtd:	MTD device structure
+ */
+static int nand_suspend(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	return nand_get_device (this, mtd, FL_PM_SUSPENDED);
+}
+
+/**
+ * nand_resume - [MTD Interface] Resume the NAND flash
+ * @mtd:	MTD device structure
+ */
+static void nand_resume(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	if (this->state == FL_PM_SUSPENDED)
+		nand_release_device(mtd);
+	else
+		printk(KERN_ERR "resume() called for the chip which is not "
+				"in suspended state\n");
+
+}
+
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd:	MTD device structure
+ * @maxchips:	Number of chips to scan for
+ *
+ * This fills out all the not initialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values. Buffers are allocated if
+ * they are not provided by the board driver
+ *
+ */
+int nand_scan (struct mtd_info *mtd, int maxchips)
+{
+	int i, nand_maf_id, nand_dev_id, busw, maf_id;
+	struct nand_chip *this = mtd->priv;
+
+	/* Get buswidth to select the correct functions*/
+	busw = this->options & NAND_BUSWIDTH_16;
+
+	/* check for proper chip_delay setup, set 20us if not */
+	if (!this->chip_delay)
+		this->chip_delay = 20;
+
+	/* check, if a user supplied command function given */
+	if (this->cmdfunc == NULL)
+		this->cmdfunc = nand_command;
+
+	/* check, if a user supplied wait function given */
+	if (this->waitfunc == NULL)
+		this->waitfunc = nand_wait;
+
+	if (!this->select_chip)
+		this->select_chip = nand_select_chip;
+	if (!this->write_byte)
+		this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
+	if (!this->read_byte)
+		this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
+	if (!this->write_word)
+		this->write_word = nand_write_word;
+	if (!this->read_word)
+		this->read_word = nand_read_word;
+	if (!this->block_bad)
+		this->block_bad = nand_block_bad;
+	if (!this->block_markbad)
+		this->block_markbad = nand_default_block_markbad;
+	if (!this->write_buf)
+		this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+	if (!this->read_buf)
+		this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+	if (!this->verify_buf)
+		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+	if (!this->scan_bbt)
+		this->scan_bbt = nand_default_bbt;
+
+	/* Select the device */
+	this->select_chip(mtd, 0);
+
+	/* Send the command for reading device ID */
+	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+
+	/* Read manufacturer and device IDs */
+	nand_maf_id = this->read_byte(mtd);
+	nand_dev_id = this->read_byte(mtd);
+
+	printk("yxa: 0x%02x:0x%02x\n", nand_maf_id, nand_dev_id);
+
+	/* Print and store flash device information */
+	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+
+		if (nand_dev_id != nand_flash_ids[i].id) 
+			continue;
+
+		if (!mtd->name) mtd->name = nand_flash_ids[i].name;
+		this->chipsize = nand_flash_ids[i].chipsize << 20;
+		
+		/* New devices have all the information in additional id bytes */
+		if (!nand_flash_ids[i].pagesize) {
+			int extid;
+			/* The 3rd id byte contains non relevant data ATM */
+			extid = this->read_byte(mtd);
+			/* The 4th id byte is the important one */
+			extid = this->read_byte(mtd);
+			/* Calc pagesize */
+			mtd->oobblock = 1024 << (extid & 0x3);
+			extid >>= 2;
+			/* Calc oobsize */
+			mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock >> 9);
+			extid >>= 2;
+			/* Calc blocksize. Blocksize is multiples of 64KiB */
+			mtd->erasesize = (64 * 1024)  << (extid & 0x03);
+			extid >>= 2;
+			/* Get buswidth information */
+			busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+		
+		} else {
+			/* Old devices have this data hardcoded in the
+			 * device id table */
+			mtd->erasesize = nand_flash_ids[i].erasesize;
+			mtd->oobblock = nand_flash_ids[i].pagesize;
+			mtd->oobsize = mtd->oobblock / 32;
+			busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
+		}
+
+		/* Try to identify manufacturer */
+		for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) {
+			if (nand_manuf_ids[maf_id].id == nand_maf_id)
+				break;
+		}
+
+		/* Check, if buswidth is correct. Hardware drivers should set
+		 * this correct ! */
+		if (busw != (this->options & NAND_BUSWIDTH_16)) {
+			printk (KERN_INFO "NAND device: Manufacturer ID:"
+				" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, 
+				nand_manuf_ids[maf_id].name , mtd->name);
+			printk (KERN_WARNING 
+				"NAND bus width %d instead %d bit\n", 
+					(this->options & NAND_BUSWIDTH_16) ? 16 : 8,
+					busw ? 16 : 8);
+			this->select_chip(mtd, -1);
+			return 1;	
+		}
+		
+		/* Calculate the address shift from the page size */	
+		this->page_shift = ffs(mtd->oobblock) - 1;
+		this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
+		this->chip_shift = ffs(this->chipsize) - 1;
+
+		/* Set the bad block position */
+		this->badblockpos = mtd->oobblock > 512 ? 
+			NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+
+		/* Get chip options, preserve non chip based options */
+		this->options &= ~NAND_CHIPOPTIONS_MSK;
+		this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
+		/* Set this as a default. Board drivers can override it, if neccecary */
+		this->options |= NAND_NO_AUTOINCR;
+		/* Check if this is a not a samsung device. Do not clear the options
+		 * for chips which are not having an extended id.
+		 */	
+		if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
+			this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+		
+		/* Check for AND chips with 4 page planes */
+		if (this->options & NAND_4PAGE_ARRAY)
+			this->erase_cmd = multi_erase_cmd;
+		else
+			this->erase_cmd = single_erase_cmd;
+
+		/* Do not replace user supplied command function ! */
+		if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
+			this->cmdfunc = nand_command_lp;
+				
+		printk (KERN_INFO "NAND device: Manufacturer ID:"
+			" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, 
+			nand_manuf_ids[maf_id].name , nand_flash_ids[i].name);
+		break;
+	}
+
+	if (!nand_flash_ids[i].name) {
+		printk (KERN_WARNING "No NAND device found!!!\n");
+		this->select_chip(mtd, -1);
+		return 1;
+	}
+
+	for (i=1; i < maxchips; i++) {
+		this->select_chip(mtd, i);
+
+		/* Send the command for reading device ID */
+		this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+
+		/* Read manufacturer and device IDs */
+		if (nand_maf_id != this->read_byte(mtd) ||
+		    nand_dev_id != this->read_byte(mtd))
+			break;
+	}
+	if (i > 1)
+		printk(KERN_INFO "%d NAND chips detected\n", i);
+	
+	/* Allocate buffers, if neccecary */
+	if (!this->oob_buf) {
+		size_t len;
+		len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
+		this->oob_buf = kmalloc (len, GFP_KERNEL);
+		if (!this->oob_buf) {
+			printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
+			return -ENOMEM;
+		}
+		this->options |= NAND_OOBBUF_ALLOC;
+	}
+	
+	if (!this->data_buf) {
+		size_t len;
+		len = mtd->oobblock + mtd->oobsize;
+		this->data_buf = kmalloc (len, GFP_KERNEL);
+		if (!this->data_buf) {
+			if (this->options & NAND_OOBBUF_ALLOC)
+				kfree (this->oob_buf);
+			printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
+			return -ENOMEM;
+		}
+		this->options |= NAND_DATABUF_ALLOC;
+	}
+
+	/* Store the number of chips and calc total size for mtd */
+	this->numchips = i;
+	mtd->size = i * this->chipsize;
+	/* Convert chipsize to number of pages per chip -1. */
+	this->pagemask = (this->chipsize >> this->page_shift) - 1;
+	/* Preset the internal oob buffer */
+	memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
+
+	/* If no default placement scheme is given, select an
+	 * appropriate one */
+	if (!this->autooob) {
+		/* Select the appropriate default oob placement scheme for
+		 * placement agnostic filesystems */
+		switch (mtd->oobsize) { 
+		case 8:
+			this->autooob = &nand_oob_8;
+			break;
+		case 16:
+			this->autooob = &nand_oob_16;
+			break;
+		case 64:
+			this->autooob = &nand_oob_64;
+			break;
+		default:
+			printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
+				mtd->oobsize);
+			BUG();
+		}
+	}
+	
+	/* The number of bytes available for the filesystem to place fs dependend
+	 * oob data */
+	mtd->oobavail = 0;
+	for (i = 0; this->autooob->oobfree[i][1]; i++)
+		mtd->oobavail += this->autooob->oobfree[i][1];
+
+	/* 
+	 * check ECC mode, default to software
+	 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
+	 * fallback to software ECC 
+	*/
+	this->eccsize = 256;	/* set default eccsize */	
+	this->eccbytes = 3;
+
+	switch (this->eccmode) {
+	case NAND_ECC_HW12_2048:
+		if (mtd->oobblock < 2048) {
+			printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
+			       mtd->oobblock);
+			this->eccmode = NAND_ECC_SOFT;
+			this->calculate_ecc = nand_calculate_ecc;
+			this->correct_data = nand_correct_data;
+		} else
+			this->eccsize = 2048;
+		break;
+
+	case NAND_ECC_HW3_512: 
+	case NAND_ECC_HW6_512: 
+	case NAND_ECC_HW8_512: 
+		if (mtd->oobblock == 256) {
+			printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
+			this->eccmode = NAND_ECC_SOFT;
+			this->calculate_ecc = nand_calculate_ecc;
+			this->correct_data = nand_correct_data;
+		} else 
+			this->eccsize = 512; /* set eccsize to 512 */
+		break;
+			
+	case NAND_ECC_HW3_256:
+		break;
+		
+	case NAND_ECC_NONE: 
+		printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
+		this->eccmode = NAND_ECC_NONE;
+		break;
+
+	case NAND_ECC_SOFT:	
+		this->calculate_ecc = nand_calculate_ecc;
+		this->correct_data = nand_correct_data;
+		break;
+
+	default:
+		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+		BUG();	
+	}	
+
+	/* Check hardware ecc function availability and adjust number of ecc bytes per 
+	 * calculation step
+	*/
+	switch (this->eccmode) {
+	case NAND_ECC_HW12_2048:
+		this->eccbytes += 4;
+	case NAND_ECC_HW8_512: 
+		this->eccbytes += 2;
+	case NAND_ECC_HW6_512: 
+		this->eccbytes += 3;
+	case NAND_ECC_HW3_512: 
+	case NAND_ECC_HW3_256:
+		if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
+			break;
+		printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
+		BUG();	
+	}
+		
+	mtd->eccsize = this->eccsize;
+	
+	/* Set the number of read / write steps for one page to ensure ECC generation */
+	switch (this->eccmode) {
+	case NAND_ECC_HW12_2048:
+		this->eccsteps = mtd->oobblock / 2048;
+		break;
+	case NAND_ECC_HW3_512:
+	case NAND_ECC_HW6_512:
+	case NAND_ECC_HW8_512:
+		this->eccsteps = mtd->oobblock / 512;
+		break;
+	case NAND_ECC_HW3_256:
+	case NAND_ECC_SOFT:	
+		this->eccsteps = mtd->oobblock / 256;
+		break;
+		
+	case NAND_ECC_NONE: 
+		this->eccsteps = 1;
+		break;
+	}
+	
+	/* Initialize state, waitqueue and spinlock */
+	this->state = FL_READY;
+	init_waitqueue_head (&this->wq);
+	spin_lock_init (&this->chip_lock);
+
+	/* De-select the device */
+	this->select_chip(mtd, -1);
+
+	/* Invalidate the pagebuffer reference */
+	this->pagebuf = -1;
+
+	/* Fill in remaining MTD driver data */
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+	mtd->ecctype = MTD_ECC_SW;
+	mtd->erase = nand_erase;
+	mtd->point = NULL;
+	mtd->unpoint = NULL;
+	mtd->read = nand_read;
+	mtd->write = nand_write;
+	mtd->read_ecc = nand_read_ecc;
+	mtd->write_ecc = nand_write_ecc;
+	mtd->read_oob = nand_read_oob;
+	mtd->write_oob = nand_write_oob;
+	mtd->readv = NULL;
+	mtd->writev = nand_writev;
+	mtd->writev_ecc = nand_writev_ecc;
+	mtd->sync = nand_sync;
+	mtd->lock = NULL;
+	mtd->unlock = NULL;
+	mtd->suspend = nand_suspend;
+	mtd->resume = nand_resume;
+	mtd->block_isbad = nand_block_isbad;
+	mtd->block_markbad = nand_block_markbad;
+
+	/* and make the autooob the default one */
+	memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
+
+	mtd->owner = THIS_MODULE;
+
+	/* Check, if we should skip the bad block table scan */
+	if (this->options & NAND_SKIP_BBTSCAN)
+		return 0;
+
+	/* Build bad block table */
+	return this->scan_bbt (mtd);
+}
+
+/**
+ * nand_release - [NAND Interface] Free resources held by the NAND device 
+ * @mtd:	MTD device structure
+*/
+void nand_release (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	/* Deregister partitions */
+	del_mtd_partitions (mtd);
+#endif
+	/* Deregister the device */
+	del_mtd_device (mtd);
+
+	/* Free bad block table memory, if allocated */
+	if (this->bbt)
+		kfree (this->bbt);
+	/* Buffer allocated by nand_scan ? */
+	if (this->options & NAND_OOBBUF_ALLOC)
+		kfree (this->oob_buf);
+	/* Buffer allocated by nand_scan ? */
+	if (this->options & NAND_DATABUF_ALLOC)
+		kfree (this->data_buf);
+}
+
+EXPORT_SYMBOL_GPL (nand_scan);
+EXPORT_SYMBOL_GPL (nand_release);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION ("Generic NAND flash driver code");
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/nand_bbt.c linux-2.4.30/drivers/mtd/nand/nand_bbt.c
--- linux-2.4.30-orig/drivers/mtd/nand/nand_bbt.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/nand/nand_bbt.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,1112 @@
+/*
+ *  drivers/mtd/nand_bbt.c
+ *
+ *  Overview:
+ *   Bad block table support for the NAND driver
+ *   
+ *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: nand_bbt.c,v 1.3 2005/10/12 14:48:40 lel Exp $
+ *
+ * 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.
+ *
+ * Description:
+ *
+ * When nand_scan_bbt is called, then it tries to find the bad block table 
+ * depending on the options in the bbt descriptor(s). If a bbt is found 
+ * then the contents are read and the memory based bbt is created. If a 
+ * mirrored bbt is selected then the mirror is searched too and the
+ * versions are compared. If the mirror has a greater version number 
+ * than the mirror bbt is used to build the memory based bbt.
+ * If the tables are not versioned, then we "or" the bad block information.
+ * If one of the bbt's is out of date or does not exist it is (re)created. 
+ * If no bbt exists at all then the device is scanned for factory marked 
+ * good / bad blocks and the bad block tables are created. 
+ *
+ * For manufacturer created bbts like the one found on M-SYS DOC devices 
+ * the bbt is searched and read but never created
+ *
+ * The autogenerated bad block table is located in the last good blocks 
+ * of the device. The table is mirrored, so it can be updated eventually. 
+ * The table is marked in the oob area with an ident pattern and a version 
+ * number which indicates which of both tables is more up to date.
+ *
+ * The table uses 2 bits per block
+ * 11b: 	block is good
+ * 00b: 	block is factory marked bad
+ * 01b, 10b: 	block is marked bad due to wear
+ *
+ * The memory bad block table uses the following scheme:
+ * 00b:		block is good
+ * 01b:		block is marked bad due to wear
+ * 10b:		block is reserved (to protect the bbt area)
+ * 11b:		block is factory marked bad
+ * 
+ * Multichip devices like DOC store the bad block info per floor.
+ *
+ * Following assumptions are made:
+ * - bbts start at a page boundary, if autolocated on a block boundary
+ * - the space neccecary for a bbt in FLASH does not exceed a block boundary
+ * 
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+
+/** 
+ * check_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf:	the buffer to search
+ * @len:	the length of buffer to search
+ * @paglen:	the pagelength
+ * @td:		search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers.
+ * If the SCAN_EMPTY option is set then check, if all bytes except the
+ * pattern area contain 0xff
+ *
+*/
+static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+	int i, end = 0;
+	uint8_t *p = buf;
+
+	end = paglen + td->offs;
+	if (td->options & NAND_BBT_SCANEMPTY) {
+		for (i = 0; i < end; i++) {
+			if (p[i] != 0xff)
+				return -1;
+		}
+	}	
+	p += end;
+	
+	/* Compare the pattern */
+	for (i = 0; i < td->len; i++) {
+		if (p[i] != td->pattern[i])
+			return -1;
+	}
+
+	if (td->options & NAND_BBT_SCANEMPTY) {
+		p += td->len;
+		end += td->len;
+		for (i = end; i < len; i++) {
+			if (*p++ != 0xff)
+				return -1;
+		}
+	}
+	return 0;
+}
+
+/** 
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf:	the buffer to search
+ * @td:		search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers. Same as check_pattern, but 
+ * no optional empty check
+ *
+*/
+static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)
+{
+	int i;
+	uint8_t *p = buf;
+
+	/* Compare the pattern */
+	for (i = 0; i < td->len; i++) {
+		if (p[td->offs + i] != td->pattern[i])
+			return -1;
+	}
+	return 0;
+}
+
+/**
+ * read_bbt - [GENERIC] Read the bad block table starting from page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @page:	the starting page
+ * @num:	the number of bbt descriptors to read
+ * @bits:	number of bits per block
+ * @offs:	offset in the memory table
+ * @reserved_block_code:	Pattern to identify reserved blocks
+ *
+ * Read the bad block table starting from page.
+ *
+ */
+static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, 
+	int bits, int offs, int reserved_block_code)
+{
+	int res, i, j, act = 0;
+	struct nand_chip *this = mtd->priv;
+	size_t retlen, len, totlen;
+	loff_t from;
+	uint8_t msk = (uint8_t) ((1 << bits) - 1);
+
+	totlen = (num * bits) >> 3;
+	from = ((loff_t)page) << this->page_shift;
+	
+	while (totlen) {
+		len = min (totlen, (size_t) (1 << this->bbt_erase_shift));
+		res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob);
+		if (res < 0) {
+			if (retlen != len) {
+				printk (KERN_INFO "nand_bbt: Error reading bad block table\n");
+				return res;
+			}
+			printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
+		}	
+
+		/* Analyse data */
+		for (i = 0; i < len; i++) {
+			uint8_t dat = buf[i];
+			for (j = 0; j < 8; j += bits, act += 2) {
+				uint8_t tmp = (dat >> j) & msk;
+				if (tmp == msk)
+					continue;
+				if (reserved_block_code &&
+				    (tmp == reserved_block_code)) {
+					printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
+						((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+					this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
+					continue;
+				}
+				/* Leave it for now, if its matured we can move this
+				 * message to MTD_DEBUG_LEVEL0 */
+				printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
+					((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+				/* Factory marked bad or worn out ? */	
+				if (tmp == 0)
+					this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
+				else
+					this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
+			}	
+		}
+		totlen -= len;
+		from += len;
+	}
+	return 0;
+}
+
+/**
+ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table 
+ * @chip:	read the table for a specific chip, -1 read all chips.
+ *		Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Read the bad block table for all chips starting at a given page
+ * We assume that the bbt bits are in consecutive order.
+*/
+static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	int res = 0, i;
+	int bits;
+
+	bits = td->options & NAND_BBT_NRBITS_MSK;
+	if (td->options & NAND_BBT_PERCHIP) {
+		int offs = 0;
+		for (i = 0; i < this->numchips; i++) {
+			if (chip == -1 || chip == i)
+				res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
+			if (res)
+				return res;
+			offs += this->chipsize >> (this->bbt_erase_shift + 2);
+		}
+	} else {
+		res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
+		if (res)
+			return res;
+	}
+	return 0;
+}
+
+/**
+ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table 
+ * @md:		descriptor for the bad block table mirror
+ *
+ * Read the bad block table(s) for all chips starting at a given page
+ * We assume that the bbt bits are in consecutive order.
+ *
+*/
+static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td,
+	struct nand_bbt_descr *md)
+{
+	struct nand_chip *this = mtd->priv;
+
+	/* Read the primary version, if available */	
+	if (td->options & NAND_BBT_VERSION) {
+		nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); 
+		td->version[0] = buf[mtd->oobblock + td->veroffs];
+		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]);
+	}
+
+	/* Read the mirror version, if available */	
+	if (md && (md->options & NAND_BBT_VERSION)) {
+		nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); 
+		md->version[0] = buf[mtd->oobblock + md->veroffs];
+		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
+	}
+
+	return 1;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @bd:		descriptor for the good/bad block search pattern
+ * @chip:	create the table for a specific chip, -1 read all chips.
+ *		Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device
+ * for the given good/bad block identify pattern
+ */
+static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, j, numblocks, len, scanlen;
+	int startblock;
+	loff_t from;
+	size_t readlen, ooblen;
+
+	printk (KERN_INFO "Scanning device for bad blocks\n");
+
+	if (bd->options & NAND_BBT_SCANALLPAGES)
+		len = 1 << (this->bbt_erase_shift - this->page_shift);
+	else {
+		if (bd->options & NAND_BBT_SCAN2NDPAGE)
+			len = 2;
+		else	
+			len = 1;
+	}
+
+	if (!(bd->options & NAND_BBT_SCANEMPTY)) {
+		/* We need only read few bytes from the OOB area */
+		scanlen = ooblen = 0;
+		readlen = bd->len;
+	} else {
+		/* Full page content should be read */
+		scanlen	= mtd->oobblock + mtd->oobsize;
+		readlen = len * mtd->oobblock;
+		ooblen = len * mtd->oobsize;
+	}
+
+	if (chip == -1) {
+		/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
+		 * makes shifting and masking less painful */
+		numblocks = mtd->size >> (this->bbt_erase_shift - 1);
+		startblock = 0;
+		from = 0;
+	} else {
+		if (chip >= this->numchips) {
+			printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+				chip + 1, this->numchips);
+			return -EINVAL;
+		}
+		numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
+		startblock = chip * numblocks;
+		numblocks += startblock;
+		from = startblock << (this->bbt_erase_shift - 1);
+	}
+	
+	for (i = startblock; i < numblocks;) {
+		int ret;
+		
+		if (bd->options & NAND_BBT_SCANEMPTY)
+			if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))
+				return ret;
+
+		for (j = 0; j < len; j++) {
+			if (!(bd->options & NAND_BBT_SCANEMPTY)) {
+				size_t retlen;
+				
+				/* Read the full oob until read_oob is fixed to 
+				 * handle single byte reads for 16 bit buswidth */
+				ret = mtd->read_oob(mtd, from + j * mtd->oobblock,
+							mtd->oobsize, &retlen, buf);
+				if (ret)
+					return ret;
+
+				if (check_short_pattern (buf, bd)) {
+					this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+					printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", 
+						i >> 1, (unsigned int) from);
+					break;
+				}
+			} else {
+				if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+					this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+					printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", 
+						i >> 1, (unsigned int) from);
+					break;
+				}
+			}
+		}
+		i += 2;
+		from += (1 << this->bbt_erase_shift);
+	}
+	return 0;
+}
+
+/**
+ * search_bbt - [GENERIC] scan the device for a specific bad block table
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table
+ *
+ * Read the bad block table by searching for a given ident pattern.
+ * Search is preformed either from the beginning up or from the end of 
+ * the device downwards. The search starts always at the start of a
+ * block.
+ * If the option NAND_BBT_PERCHIP is given, each chip is searched 
+ * for a bbt, which contains the bad block information of this chip.
+ * This is neccecary to provide support for certain DOC devices.
+ *
+ * The bbt ident pattern resides in the oob area of the first page 
+ * in a block. 
+ */
+static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, chips;
+	int bits, startblock, block, dir;
+	int scanlen = mtd->oobblock + mtd->oobsize;
+	int bbtblocks;
+
+	/* Search direction top -> down ? */
+	if (td->options & NAND_BBT_LASTBLOCK) {
+		startblock = (mtd->size >> this->bbt_erase_shift) -1;
+		dir = -1;
+	} else {
+		startblock = 0;	
+		dir = 1;
+	}	
+	
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) {
+		chips = this->numchips;
+		bbtblocks = this->chipsize >> this->bbt_erase_shift;
+		startblock &= bbtblocks - 1;
+	} else {
+		chips = 1;
+		bbtblocks = mtd->size >> this->bbt_erase_shift;
+	}
+	
+	/* Number of bits for each erase block in the bbt */
+	bits = td->options & NAND_BBT_NRBITS_MSK;
+	
+	for (i = 0; i < chips; i++) {
+		/* Reset version information */
+		td->version[i] = 0;	
+		td->pages[i] = -1;
+		/* Scan the maximum number of blocks */
+		for (block = 0; block < td->maxblocks; block++) {
+			int actblock = startblock + dir * block;
+			/* Read first page */
+			nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); 
+			if (!check_pattern(buf, scanlen, mtd->oobblock, td)) {
+				td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
+				if (td->options & NAND_BBT_VERSION) {
+					td->version[i] = buf[mtd->oobblock + td->veroffs];
+				}
+				break;
+			}
+		}
+		startblock += this->chipsize >> this->bbt_erase_shift;
+	}
+	/* Check, if we found a bbt for each requested chip */
+	for (i = 0; i < chips; i++) {
+		if (td->pages[i] == -1)
+			printk (KERN_WARNING "Bad block table not found for chip %d\n", i);
+		else
+			printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]);
+	}
+	return 0;	
+}
+
+/**
+ * search_read_bbts - [GENERIC] scan the device for bad block table(s)
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table 
+ * @md:		descriptor for the bad block table mirror
+ *
+ * Search and read the bad block table(s)
+*/
+static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, 
+	struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+	/* Search the primary table */
+	search_bbt (mtd, buf, td);
+		
+	/* Search the mirror table */
+	if (md)
+		search_bbt (mtd, buf, md);
+	
+	/* Force result check */
+	return 1;	
+}
+	
+
+/** 
+ * write_bbt - [GENERIC] (Re)write the bad block table
+ *
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table 
+ * @md:		descriptor for the bad block table mirror
+ * @chipsel:	selector for a specific chip, -1 for all
+ *
+ * (Re)write the bad block table
+ *
+*/
+static int write_bbt (struct mtd_info *mtd, uint8_t *buf, 
+	struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel)
+{
+	struct nand_chip *this = mtd->priv;
+	struct nand_oobinfo oobinfo;
+	struct erase_info einfo;
+	int i, j, res, chip = 0;
+	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+	int nrchips, bbtoffs, pageoffs;
+	uint8_t msk[4];
+	uint8_t rcode = td->reserved_block_code;
+	size_t retlen, len = 0;
+	loff_t to;
+
+	if (!rcode)
+		rcode = 0xff;
+	/* Write bad block table per chip rather than per device ? */
+	if (td->options & NAND_BBT_PERCHIP) {
+		numblocks = (int) (this->chipsize >> this->bbt_erase_shift);
+		/* Full device write or specific chip ? */	
+		if (chipsel == -1) {
+			nrchips = this->numchips;
+		} else {
+			nrchips = chipsel + 1;
+			chip = chipsel;
+		}
+	} else {
+		numblocks = (int) (mtd->size >> this->bbt_erase_shift);
+		nrchips = 1;
+	}	
+	
+	/* Loop through the chips */
+	for (; chip < nrchips; chip++) {
+		
+		/* There was already a version of the table, reuse the page 
+		 * This applies for absolute placement too, as we have the 
+		 * page nr. in td->pages.
+		 */
+		if (td->pages[chip] != -1) {
+			page = td->pages[chip];
+			goto write;
+		}	
+
+		/* Automatic placement of the bad block table */
+		/* Search direction top -> down ? */
+		if (td->options & NAND_BBT_LASTBLOCK) {
+			startblock = numblocks * (chip + 1) - 1;
+			dir = -1;
+		} else {
+			startblock = chip * numblocks;
+			dir = 1;
+		}	
+
+		for (i = 0; i < td->maxblocks; i++) {
+			int block = startblock + dir * i;
+			/* Check, if the block is bad */
+			switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) {
+			case 0x01:
+			case 0x03:
+				continue;
+			}
+			page = block << (this->bbt_erase_shift - this->page_shift);
+			/* Check, if the block is used by the mirror table */
+			if (!md || md->pages[chip] != page)
+				goto write;
+		}
+		printk (KERN_ERR "No space left to write bad block table\n");
+		return -ENOSPC;
+write:	
+
+		/* Set up shift count and masks for the flash table */
+		bits = td->options & NAND_BBT_NRBITS_MSK;
+		switch (bits) {
+		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break;
+		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break;
+		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break;
+		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break;
+		default: return -EINVAL;
+		}
+		
+		bbtoffs = chip * (numblocks >> 2);
+		
+		to = ((loff_t) page) << this->page_shift;
+
+		memcpy (&oobinfo, this->autooob, sizeof(oobinfo));
+		oobinfo.useecc = MTD_NANDECC_PLACEONLY;
+		
+		/* Must we save the block contents ? */
+		if (td->options & NAND_BBT_SAVECONTENT) {
+			/* Make it block aligned */
+			to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+			len = 1 << this->bbt_erase_shift;
+			res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+			if (res < 0) {
+				if (retlen != len) {
+					printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n");
+					return res;
+				}
+				printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n");
+			}
+			/* Calc the byte offset in the buffer */
+			pageoffs = page - (int)(to >> this->page_shift);
+			offs = pageoffs << this->page_shift;
+			/* Preset the bbt area with 0xff */
+			memset (&buf[offs], 0xff, (size_t)(numblocks >> sft));
+			/* Preset the bbt's oob area with 0xff */
+			memset (&buf[len + pageoffs * mtd->oobsize], 0xff,
+				((len >> this->page_shift) - pageoffs) * mtd->oobsize);
+			if (td->options & NAND_BBT_VERSION) {
+				buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip];
+			}
+		} else {
+			/* Calc length */
+			len = (size_t) (numblocks >> sft);
+			/* Make it page aligned ! */
+			len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1);
+			/* Preset the buffer with 0xff */
+			memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize);
+			offs = 0;
+			/* Pattern is located in oob area of first page */
+			memcpy (&buf[len + td->offs], td->pattern, td->len);
+			if (td->options & NAND_BBT_VERSION) {
+				buf[len + td->veroffs] = td->version[chip];
+			}
+		}
+	
+		/* walk through the memory table */
+		for (i = 0; i < numblocks; ) {
+			uint8_t dat;
+			dat = this->bbt[bbtoffs + (i >> 2)];
+			for (j = 0; j < 4; j++ , i++) {
+				int sftcnt = (i << (3 - sft)) & sftmsk;
+				/* Do not store the reserved bbt blocks ! */
+				buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt);
+				dat >>= 2;
+			}
+		}
+		
+		memset (&einfo, 0, sizeof (einfo));
+		einfo.mtd = mtd;
+		einfo.addr = (unsigned long) to;
+		einfo.len = 1 << this->bbt_erase_shift;
+		res = nand_erase_nand (mtd, &einfo, 1);
+		if (res < 0) {
+			printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res);
+			return res;
+		}
+	
+		res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+		if (res < 0) {
+			printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res);
+			return res;
+		}
+		printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", 
+			(unsigned int) to, td->version[chip]);
+	
+		/* Mark it as used */
+		td->pages[chip] = page;
+	}	
+	return 0;
+}
+
+/**
+ * nand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @mtd:	MTD device structure
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device 
+ * for manufacturer / software marked good / bad blocks
+*/
+static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	bd->options &= ~NAND_BBT_SCANEMPTY;
+	return create_bbt (mtd, this->data_buf, bd, -1);
+}
+
+/**
+ * check_create - [GENERIC] create and write bbt(s) if neccecary
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function checks the results of the previous call to read_bbt
+ * and creates / updates the bbt(s) if neccecary
+ * Creation is neccecary if no bbt was found for the chip/device
+ * Update is neccecary if one of the tables is missing or the
+ * version nr. of one table is less than the other
+*/
+static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+{
+	int i, chips, writeops, chipsel, res;
+	struct nand_chip *this = mtd->priv;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+	struct nand_bbt_descr *rd, *rd2;
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) 
+		chips = this->numchips;
+	else 
+		chips = 1;
+	
+	for (i = 0; i < chips; i++) {
+		writeops = 0;
+		rd = NULL;
+		rd2 = NULL;
+		/* Per chip or per device ? */
+		chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
+		/* Mirrored table avilable ? */
+		if (md) {
+			if (td->pages[i] == -1 && md->pages[i] == -1) {
+				writeops = 0x03;
+				goto create;
+			}
+
+			if (td->pages[i] == -1) {
+				rd = md;				
+				td->version[i] = md->version[i];
+				writeops = 1;
+				goto writecheck;
+			}
+
+			if (md->pages[i] == -1) {
+				rd = td;
+				md->version[i] = td->version[i];
+				writeops = 2;
+				goto writecheck;
+			}
+
+			if (td->version[i] == md->version[i]) {
+				rd = td;
+				if (!(td->options & NAND_BBT_VERSION))
+					rd2 = md;
+				goto writecheck;
+			}	
+
+			if (((int8_t) (td->version[i] - md->version[i])) > 0) {
+				rd = td;
+				md->version[i] = td->version[i];
+				writeops = 2;
+			} else {
+				rd = md;
+				td->version[i] = md->version[i];
+				writeops = 1;
+			}
+
+			goto writecheck;
+
+		} else {
+			if (td->pages[i] == -1) {
+				writeops = 0x01;
+				goto create;
+			}
+			rd = td;
+			goto writecheck;
+		}
+create:
+		/* Create the bad block table by scanning the device ? */
+		if (!(td->options & NAND_BBT_CREATE))
+			continue;	
+		
+		/* Create the table in memory by scanning the chip(s) */
+		create_bbt (mtd, buf, bd, chipsel);
+		
+		td->version[i] = 1;
+		if (md)
+			md->version[i] = 1;	
+writecheck:	
+		/* read back first ? */
+		if (rd)
+			read_abs_bbt (mtd, buf, rd, chipsel);
+		/* If they weren't versioned, read both. */
+		if (rd2)
+			read_abs_bbt (mtd, buf, rd2, chipsel);
+
+		/* Write the bad block table to the device ? */
+		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+			res = write_bbt (mtd, buf, td, md, chipsel);
+			if (res < 0)
+				return res;
+		}
+		
+		/* Write the mirror bad block table to the device ? */
+		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+			res = write_bbt (mtd, buf, md, td, chipsel);
+			if (res < 0)
+				return res;
+		}
+	}
+	return 0;	
+}
+
+/**
+ * mark_bbt_regions - [GENERIC] mark the bad block table regions 
+ * @mtd:	MTD device structure
+ * @td:		bad block table descriptor
+ *
+ * The bad block table regions are marked as "bad" to prevent
+ * accidental erasures / writes. The regions are identified by
+ * the mark 0x02.
+*/
+static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, j, chips, block, nrblocks, update;
+	uint8_t oldval, newval;
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) {
+		chips = this->numchips;
+		nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+	} else {
+		chips = 1;
+		nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+	}	
+	
+	for (i = 0; i < chips; i++) {
+		if ((td->options & NAND_BBT_ABSPAGE) ||
+		    !(td->options & NAND_BBT_WRITE)) {
+		    	if (td->pages[i] == -1) continue;
+			block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+			block <<= 1;		
+			oldval = this->bbt[(block >> 3)];
+			newval = oldval | (0x2 << (block & 0x06));
+			this->bbt[(block >> 3)] = newval;
+			if ((oldval != newval) && td->reserved_block_code)
+				nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1));
+			continue;
+		}
+		update = 0;
+		if (td->options & NAND_BBT_LASTBLOCK)
+			block = ((i + 1) * nrblocks) - td->maxblocks;
+		else	
+			block = i * nrblocks;
+		block <<= 1;	
+		for (j = 0; j < td->maxblocks; j++) {
+			oldval = this->bbt[(block >> 3)];
+			newval = oldval | (0x2 << (block & 0x06));
+			this->bbt[(block >> 3)] = newval;
+			if (oldval != newval) update = 1;
+			block += 2;
+		}	
+		/* If we want reserved blocks to be recorded to flash, and some
+		   new ones have been marked, then we need to update the stored
+		   bbts.  This should only happen once. */
+		if (update && td->reserved_block_code)
+			nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1));
+	}
+}
+
+/**
+ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
+ * @mtd:	MTD device structure
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already 
+ * available. If not it scans the device for manufacturer
+ * marked good / bad blocks and writes the bad block table(s) to
+ * the selected place.
+ *
+ * The bad block table memory is allocated here. It must be freed
+ * by calling the nand_free_bbt function.
+ *
+*/
+int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+	struct nand_chip *this = mtd->priv;
+	int len, res = 0;
+	uint8_t *buf;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+
+	len = mtd->size >> (this->bbt_erase_shift + 2);
+	/* Allocate memory (2bit per block) */
+	this->bbt = kmalloc (len, GFP_KERNEL);
+	if (!this->bbt) {
+		printk (KERN_ERR "nand_scan_bbt: Out of memory\n");
+		return -ENOMEM;
+	}
+	/* Clear the memory bad block table */
+	memset (this->bbt, 0x00, len);
+
+	/* If no primary table decriptor is given, scan the device
+	 * to build a memory based bad block table
+	 */
+	if (!td) {
+		if ((res = nand_memory_bbt(mtd, bd))) {
+			printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
+			kfree (this->bbt);
+			this->bbt = NULL;
+		}
+		return res;
+	}
+
+	/* Allocate a temporary buffer for one eraseblock incl. oob */
+	len = (1 << this->bbt_erase_shift);
+	len += (len >> this->page_shift) * mtd->oobsize;
+	buf = kmalloc (len, GFP_KERNEL);
+	if (!buf) {
+		printk (KERN_ERR "nand_bbt: Out of memory\n");
+		kfree (this->bbt);
+		this->bbt = NULL;
+		return -ENOMEM;
+	}
+	
+	/* Is the bbt at a given page ? */
+	if (td->options & NAND_BBT_ABSPAGE) {
+		res = read_abs_bbts (mtd, buf, td, md);
+	} else {	
+		/* Search the bad block table using a pattern in oob */
+		res = search_read_bbts (mtd, buf, td, md);
+	}	
+
+	if (res) 
+		res = check_create (mtd, buf, bd);
+	
+	/* Prevent the bbt regions from erasing / writing */
+	mark_bbt_region (mtd, td);
+	if (md)
+		mark_bbt_region (mtd, md);
+	
+	kfree (buf);
+	return res;
+}
+
+
+/**
+ * nand_update_bbt - [NAND Interface] update bad block table(s) 
+ * @mtd:	MTD device structure
+ * @offs:	the offset of the newly marked block
+ *
+ * The function updates the bad block table(s)
+*/
+int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_chip *this = mtd->priv;
+	int len, res = 0, writeops = 0;
+	int chip, chipsel;
+	uint8_t *buf;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+
+	if (!this->bbt || !td)
+		return -EINVAL;
+
+	len = mtd->size >> (this->bbt_erase_shift + 2);
+	/* Allocate a temporary buffer for one eraseblock incl. oob */
+	len = (1 << this->bbt_erase_shift);
+	len += (len >> this->page_shift) * mtd->oobsize;
+	buf = kmalloc (len, GFP_KERNEL);
+	if (!buf) {
+		printk (KERN_ERR "nand_update_bbt: Out of memory\n");
+		return -ENOMEM;
+	}
+	
+	writeops = md != NULL ? 0x03 : 0x01;
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) {
+		chip = (int) (offs >> this->chip_shift);
+		chipsel = chip;
+	} else {
+		chip = 0;
+		chipsel = -1;
+	}
+
+	td->version[chip]++;
+	if (md)
+		md->version[chip]++;	
+
+	/* Write the bad block table to the device ? */
+	if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+		res = write_bbt (mtd, buf, td, md, chipsel);
+		if (res < 0)
+			goto out;
+	}
+	/* Write the mirror bad block table to the device ? */
+	if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+		res = write_bbt (mtd, buf, md, td, chipsel);
+	}
+
+out:	
+	kfree (buf);
+	return res;
+}
+
+/* Define some generic bad / good block scan pattern which are used 
+ * while scanning a device for factory marked good / bad blocks. */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+	.options = NAND_BBT_SCAN2NDPAGE,
+	.offs = 5,
+	.len = 1,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_memorybased = {
+	.options = 0,
+	.offs = 0,
+	.len = 2,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr smallpage_flashbased = {
+	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.offs = 5,
+	.len = 1,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_flashbased = {
+	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.offs = 0,
+	.len = 2,
+	.pattern = scan_ff_pattern
+};
+
+static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
+
+static struct nand_bbt_descr agand_flashbased = {
+	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.offs = 0x20,
+	.len = 6,
+	.pattern = scan_agand_pattern
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = 4,
+	.pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = 4,
+	.pattern = mirror_pattern
+};
+
+/**
+ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device 
+ * @mtd:	MTD device structure
+ *
+ * This function selects the default bad block table
+ * support for the device and calls the nand_scan_bbt function
+ *
+*/
+int nand_default_bbt (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	
+	/* Default for AG-AND. We must use a flash based 
+	 * bad block table as the devices have factory marked
+	 * _good_ blocks. Erasing those blocks leads to loss
+	 * of the good / bad information, so we _must_ store
+	 * this information in a good / bad table during 
+	 * startup
+	*/
+	if (this->options & NAND_IS_AND) {
+		/* Use the default pattern descriptors */
+		if (!this->bbt_td) {	
+			this->bbt_td = &bbt_main_descr;
+			this->bbt_md = &bbt_mirror_descr;
+		}	
+		this->options |= NAND_USE_FLASH_BBT;
+		return nand_scan_bbt (mtd, &agand_flashbased);
+	}
+	
+	
+	/* Is a flash based bad block table requested ? */
+	if (this->options & NAND_USE_FLASH_BBT) {
+		/* Use the default pattern descriptors */	
+		if (!this->bbt_td) {	
+			this->bbt_td = &bbt_main_descr;
+			this->bbt_md = &bbt_mirror_descr;
+		}
+		if (!this->badblock_pattern) {
+			this->badblock_pattern = (mtd->oobblock > 512) ?
+				&largepage_flashbased : &smallpage_flashbased;
+		}
+	} else {
+		this->bbt_td = NULL;
+		this->bbt_md = NULL;
+		if (!this->badblock_pattern) {
+			this->badblock_pattern = (mtd->oobblock > 512) ?
+				&largepage_memorybased : &smallpage_memorybased;
+		}
+	}
+	return nand_scan_bbt (mtd, this->badblock_pattern);
+}
+
+/**
+ * nand_isbad_bbt - [NAND Interface] Check if a block is bad 
+ * @mtd:	MTD device structure
+ * @offs:	offset in the device
+ * @allowbbt:	allow access to bad block table region
+ *
+*/
+int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+	struct nand_chip *this = mtd->priv;
+	int block;
+	uint8_t	res;
+	
+	/* Get block number * 2 */
+	block = (int) (offs >> (this->bbt_erase_shift - 1));
+	res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+
+	DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", 
+		(unsigned int)offs, block >> 1, res);
+
+	switch ((int)res) {
+	case 0x00:	return 0;
+	case 0x01:	return 1;
+	case 0x02:	return allowbbt ? 0 : 1;
+	}
+	return 1;
+}
+
+EXPORT_SYMBOL (nand_scan_bbt);
+EXPORT_SYMBOL (nand_default_bbt);
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/nand_ecc.c linux-2.4.30/drivers/mtd/nand/nand_ecc.c
--- linux-2.4.30-orig/drivers/mtd/nand/nand_ecc.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/nand/nand_ecc.c	2006-12-11 18:18:37.000000000 +0100
@@ -7,7 +7,7 @@
  * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
  *                         Toshiba America Electronics Components, Inc.
  *
- * $Id: nand_ecc.c,v 1.11 2004/05/05 14:19:42 sjhill Exp $
+ * $Id: nand_ecc.c,v 1.3 2005/10/12 14:48:40 lel Exp $
  *
  * This file is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -63,7 +63,12 @@
 };
 
 
-/*
+/**
+ * nand_trans_result - [GENERIC] create non-inverted ECC
+ * @reg2:	line parity reg 2
+ * @reg3:	line parity reg 3
+ * @ecc_code:	ecc 
+ *
  * Creates non-inverted ECC code from line parity
  */
 static void nand_trans_result(u_char reg2, u_char reg3,
@@ -103,10 +108,13 @@
 	ecc_code[1] = tmp2;
 }
 
-/*
- * Calculate 3 byte ECC code for 256 byte block
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block
+ * @mtd:	MTD block structure
+ * @dat:	raw data
+ * @ecc_code:	buffer for ECC
  */
-void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
 {
 	u_char idx, reg1, reg2, reg3;
 	int j;
@@ -136,9 +144,16 @@
 	ecc_code[0] = ~ecc_code[0];
 	ecc_code[1] = ~ecc_code[1];
 	ecc_code[2] = ((~reg1) << 2) | 0x03;
+	return 0;
 }
 
-/*
+/**
+ * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd:	MTD block structure
+ * @dat:	raw data read from the chip
+ * @read_ecc:	ECC from the chip
+ * @calc_ecc:	the ECC calculated from raw data
+ *
  * Detect and correct a 1 bit error for 256 byte block
  */
 int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/nand_ids.c linux-2.4.30/drivers/mtd/nand/nand_ids.c
--- linux-2.4.30-orig/drivers/mtd/nand/nand_ids.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/drivers/mtd/nand/nand_ids.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,7 +3,7 @@
  *
  *  Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
   *
- * $Id: nand_ids.c,v 1.7 2004/03/28 23:31:28 gleixner Exp $
+ * $Id: nand_ids.c,v 1.3 2005/10/12 14:48:41 lel Exp $
  *
  * 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
@@ -56,23 +56,30 @@
 	{"NAND 64MiB 3,3V 16-bit", 	0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
 	
 	{"NAND 128MiB 1,8V 8-bit", 	0x78, 512, 128, 0x4000, 0},
+	{"NAND 128MiB 1,8V 8-bit", 	0x39, 512, 128, 0x4000, 0},
 	{"NAND 128MiB 3,3V 8-bit", 	0x79, 512, 128, 0x4000, 0},
 	{"NAND 128MiB 1,8V 16-bit", 	0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 128MiB 1,8V 16-bit", 	0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
 	{"NAND 128MiB 3,3V 16-bit", 	0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 128MiB 3,3V 16-bit", 	0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
 	
 	{"NAND 256MiB 3,3V 8-bit", 	0x71, 512, 256, 0x4000, 0},
 
-	{"NAND 512MiB 3,3V 8-bit", 	0xDC, 512, 512, 0x4000, 0},
-	
 	/* These are the new chips with large page size. The pagesize
 	* and the erasesize is determined from the extended id bytes
 	*/
+	/*512 Megabit */
+	{"NAND 64MiB 1,8V 8-bit", 	0xA2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+	{"NAND 64MiB 3,3V 8-bit", 	0xF2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+	{"NAND 64MiB 1,8V 16-bit", 	0xB2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 64MiB 3,3V 16-bit", 	0xC2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	
 	/* 1 Gigabit */
 	{"NAND 128MiB 1,8V 8-bit", 	0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
 	{"NAND 128MiB 3,3V 8-bit", 	0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
 	{"NAND 128MiB 1,8V 16-bit", 	0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
 	{"NAND 128MiB 3,3V 16-bit", 	0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-	
+
 	/* 2 Gigabit */
 	{"NAND 256MiB 1,8V 8-bit", 	0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
 	{"NAND 256MiB 3,3V 8-bit", 	0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
@@ -96,7 +103,15 @@
 	{"NAND 2GiB 3,3V 8-bit", 	0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
 	{"NAND 2GiB 1,8V 16-bit", 	0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
 	{"NAND 2GiB 3,3V 16-bit", 	0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
-	
+
+	/* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! 
+	 * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes
+	 * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7
+	 * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go
+	 * There are more speed improvements for reads and writes possible, but not implemented now 
+	 */
+	{"AND 128MiB 3,3V 8-bit",	0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
+
 	{NULL,}
 };
 
@@ -108,6 +123,9 @@
 	{NAND_MFR_SAMSUNG, "Samsung"},
 	{NAND_MFR_FUJITSU, "Fujitsu"},
 	{NAND_MFR_NATIONAL, "National"},
+	{NAND_MFR_RENESAS, "Renesas"},
+	{NAND_MFR_STMICRO, "ST Micro"},
+        {NAND_MFR_HYNIX, "Hynix"},
 	{0x0, "Unknown"}
 };
 
diff -x asm -Naur linux-2.4.30-orig/drivers/mtd/nand/nandsim.c linux-2.4.30/drivers/mtd/nand/nandsim.c
--- linux-2.4.30-orig/drivers/mtd/nand/nandsim.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/drivers/mtd/nand/nandsim.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,1609 @@
+/*
+ * NAND flash simulator.
+ *
+ * Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org>
+ *
+ * Copyright (C) 2004 Nokia Corporation 
+ *
+ * Note: NS means "NAND Simulator".
+ * Note: Input means input TO flash chip, output means output FROM chip.
+ *
+ * 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, 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
+ *
+ * $Id: nandsim.c,v 1.3 2005/10/12 14:48:41 lel Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+
+#ifdef CONFIG_NS_ABS_POS
+#include <asm/io.h>
+#endif
+
+#define CONFIG_NANDSIM_FIRST_ID_BYTE        CONFIG_MTD_NAND_NANDSIM_FIRST_ID
+#define CONFIG_NANDSIM_SECOND_ID_BYTE       CONFIG_MTD_NAND_NANDSIM_SECOND_ID
+#define CONFIG_NANDSIM_THIRD_ID_BYTE        CONFIG_MTD_NAND_NANDSIM_THIRD_ID
+#define CONFIG_NANDSIM_FOURTH_ID_BYTE       CONFIG_MTD_NAND_NANDSIM_FOURTH_ID
+
+#ifndef CONFIG_MTD_NAND_NANDSIM_ACCESS_DELAY
+#  define CONFIG_MTD_NAND_NANDSIM_ACCESS_DELAY   25
+#endif
+
+#ifndef CONFIG_MTD_NAND_NANDSIM_PROGRAM_DELAY
+#  define CONFIG_MTD_NAND_NANDSIM_PROGRAM_DELAY  200
+#endif
+
+#ifndef CONFIG_MTD_NAND_NANDSIM_ERASE_DELAY
+#  define CONFIG_MTD_NAND_NANDSIM_ERASE_DELAY    2
+#endif
+
+#ifndef CONFIG_MTD_NAND_NANDSIM_OUTPUT_CYCLE
+#  define CONFIG_MTD_NAND_NANDSIM_OUTPUT_CYCLE   40
+#endif
+
+#ifndef CONFIG_MTD_NAND_NANDSIM_INPUT_CYCLE
+#  define CONFIG_MTD_NAND_NANDSIM_INPUT_CYCLE    50
+#endif
+
+#ifdef CONFIG_MTD_NAND_NANDSIM_16BIT_BUS
+#  define CONFIG_NANDSIM_BUS_WIDTH      16
+#else
+#  define CONFIG_NANDSIM_BUS_WIDTH      8
+#endif
+
+#ifndef CONFIG_MTD_NAND_NANDSIM_DELAY
+#  define CONFIG_MTD_NAND_NANDSIM_DELAY 0
+#else
+#  undef CONFIG_MTD_NAND_NANDSIM_DELAY
+#  define CONFIG_MTD_NAND_NANDSIM_DELAY 1
+#endif
+
+#ifndef CONFIG_MTD_NAND_NANDSIM_LOG
+#  define CONFIG_MTD_NAND_NANDSIM_LOG   0
+#else
+#  undef CONFIG_MTD_NAND_NANDSIM_LOG
+#  define CONFIG_MTD_NAND_NANDSIM_LOG   1
+#endif
+
+#ifndef CONFIG_MTD_NAND_NANDSIM_DEBUG
+#  define CONFIG_MTD_NAND_NANDSIM_DEBUG 0
+#else
+#  undef CONFIG_MTD_NAND_NANDSIM_DEBUG
+#  define CONFIG_MTD_NAND_NANDSIM_DEBUG 1
+#endif
+
+static uint first_id_byte  = CONFIG_NANDSIM_FIRST_ID_BYTE;
+static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE;
+static uint third_id_byte  = CONFIG_NANDSIM_THIRD_ID_BYTE;
+static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE;
+static uint access_delay   = CONFIG_MTD_NAND_NANDSIM_ACCESS_DELAY;
+static uint programm_delay = CONFIG_MTD_NAND_NANDSIM_PROGRAM_DELAY;
+static uint erase_delay    = CONFIG_MTD_NAND_NANDSIM_ERASE_DELAY;
+static uint output_cycle   = CONFIG_MTD_NAND_NANDSIM_OUTPUT_CYCLE;
+static uint input_cycle    = CONFIG_MTD_NAND_NANDSIM_INPUT_CYCLE;
+static uint bus_width      = CONFIG_NANDSIM_BUS_WIDTH;
+static uint do_delays      = CONFIG_MTD_NAND_NANDSIM_DELAY;
+static uint log            = CONFIG_MTD_NAND_NANDSIM_LOG;
+static uint dbg            = CONFIG_MTD_NAND_NANDSIM_DEBUG;
+
+module_param(first_id_byte,  uint, 0400);
+module_param(second_id_byte, uint, 0400);
+module_param(third_id_byte,  uint, 0400);
+module_param(fourth_id_byte, uint, 0400);
+module_param(access_delay,   uint, 0400);
+module_param(programm_delay, uint, 0400);
+module_param(erase_delay,    uint, 0400);
+module_param(output_cycle,   uint, 0400);
+module_param(input_cycle,    uint, 0400);
+module_param(bus_width,      uint, 0400);
+module_param(do_delays,      uint, 0400);
+module_param(log,            uint, 0400);
+module_param(dbg,            uint, 0400);
+
+MODULE_PARM_DESC(first_id_byte,  "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)");
+MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
+MODULE_PARM_DESC(third_id_byte,  "The third byte returned by NAND Flash 'read ID' command");
+MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
+MODULE_PARM_DESC(access_delay,   "Initial page access delay (microiseconds)");
+MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
+MODULE_PARM_DESC(erase_delay,    "Sector erase delay (milliseconds)");
+MODULE_PARM_DESC(output_cycle,   "Word output (from flash) time (nanoseconds)");
+MODULE_PARM_DESC(input_cycle,    "Word input (to flash) time (nanoseconds)");
+MODULE_PARM_DESC(bus_width,      "Chip's bus width (8- or 16-bit)");
+MODULE_PARM_DESC(do_delays,      "Simulate NAND delays using busy-waits if not zero");
+MODULE_PARM_DESC(log,            "Perform logging if not zero");
+MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
+
+/* The largest possible page size */
+#define NS_LARGEST_PAGE_SIZE	2048
+	
+/* The prefix for simulator output */
+#define NS_OUTPUT_PREFIX "[nandsim]"
+
+/* Simulator's output macros (logging, debugging, warning, error) */
+#define NS_LOG(args...) \
+	do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0)
+#define NS_DBG(args...) \
+	do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
+#define NS_WARN(args...) \
+	do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0)
+#define NS_ERR(args...) \
+	do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0)
+
+/* Busy-wait delay macros (microseconds, milliseconds) */
+#define NS_UDELAY(us) \
+        do { if (do_delays) udelay(us); } while(0)
+#define NS_MDELAY(us) \
+        do { if (do_delays) mdelay(us); } while(0)
+	
+/* Is the nandsim structure initialized ? */
+#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0)
+
+/* Good operation completion status */
+#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))
+
+/* Operation failed completion status */
+#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) 
+
+/* Calculate the page offset in flash RAM image by (row, column) address */
+#define NS_RAW_OFFSET(ns) \
+	(((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column)
+	
+/* Calculate the OOB offset in flash RAM image by (row, column) address */
+#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
+
+/* After a command is input, the simulator goes to one of the following states */
+#define STATE_CMD_READ0        0x00000001 /* read data from the beginning of page */
+#define STATE_CMD_READ1        0x00000002 /* read data from the second half of page */
+#define STATE_CMD_READSTART    0x00000003 /* read data second command (large page devices) */
+#define STATE_CMD_PAGEPROG     0x00000004 /* start page programm */
+#define STATE_CMD_READOOB      0x00000005 /* read OOB area */
+#define STATE_CMD_ERASE1       0x00000006 /* sector erase first command */
+#define STATE_CMD_STATUS       0x00000007 /* read status */
+#define STATE_CMD_STATUS_M     0x00000008 /* read multi-plane status (isn't implemented) */
+#define STATE_CMD_SEQIN        0x00000009 /* sequential data imput */
+#define STATE_CMD_READID       0x0000000A /* read ID */
+#define STATE_CMD_ERASE2       0x0000000B /* sector erase second command */
+#define STATE_CMD_RESET        0x0000000C /* reset */
+#define STATE_CMD_MASK         0x0000000F /* command states mask */
+
+/* After an addres is input, the simulator goes to one of these states */
+#define STATE_ADDR_PAGE        0x00000010 /* full (row, column) address is accepted */
+#define STATE_ADDR_SEC         0x00000020 /* sector address was accepted */
+#define STATE_ADDR_ZERO        0x00000030 /* one byte zero address was accepted */
+#define STATE_ADDR_MASK        0x00000030 /* address states mask */
+
+/* Durind data input/output the simulator is in these states */
+#define STATE_DATAIN           0x00000100 /* waiting for data input */
+#define STATE_DATAIN_MASK      0x00000100 /* data input states mask */
+
+#define STATE_DATAOUT          0x00001000 /* waiting for page data output */
+#define STATE_DATAOUT_ID       0x00002000 /* waiting for ID bytes output */
+#define STATE_DATAOUT_STATUS   0x00003000 /* waiting for status output */
+#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */
+#define STATE_DATAOUT_MASK     0x00007000 /* data output states mask */
+
+/* Previous operation is done, ready to accept new requests */
+#define STATE_READY            0x00000000
+
+/* This state is used to mark that the next state isn't known yet */
+#define STATE_UNKNOWN          0x10000000
+
+/* Simulator's actions bit masks */
+#define ACTION_CPY       0x00100000 /* copy page/OOB to the internal buffer */
+#define ACTION_PRGPAGE   0x00200000 /* programm the internal buffer to flash */
+#define ACTION_SECERASE  0x00300000 /* erase sector */
+#define ACTION_ZEROOFF   0x00400000 /* don't add any offset to address */
+#define ACTION_HALFOFF   0x00500000 /* add to address half of page */
+#define ACTION_OOBOFF    0x00600000 /* add to address OOB offset */
+#define ACTION_MASK      0x00700000 /* action mask */
+
+#define NS_OPER_NUM      12 /* Number of operations supported by the simulator */
+#define NS_OPER_STATES   6  /* Maximum number of states in operation */
+
+#define OPT_ANY          0xFFFFFFFF /* any chip supports this operation */
+#define OPT_PAGE256      0x00000001 /* 256-byte  page chips */
+#define OPT_PAGE512      0x00000002 /* 512-byte  page chips */
+#define OPT_PAGE2048     0x00000008 /* 2048-byte page chips */
+#define OPT_SMARTMEDIA   0x00000010 /* SmartMedia technology chips */
+#define OPT_AUTOINCR     0x00000020 /* page number auto inctimentation is possible */
+#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
+#define OPT_LARGEPAGE    (OPT_PAGE2048) /* 2048-byte page chips */
+#define OPT_SMALLPAGE    (OPT_PAGE256  | OPT_PAGE512)  /* 256 and 512-byte page chips */
+
+/* Remove action bits ftom state */
+#define NS_STATE(x) ((x) & ~ACTION_MASK)
+	
+/* 
+ * Maximum previous states which need to be saved. Currently saving is
+ * only needed for page programm operation with preceeded read command
+ * (which is only valid for 512-byte pages).
+ */
+#define NS_MAX_PREVSTATES 1
+
+/* 
+ * The structure which describes all the internal simulator data.
+ */
+struct nandsim {
+	struct mtd_partition part;
+
+	uint busw;              /* flash chip bus width (8 or 16) */
+	u_char ids[4];          /* chip's ID bytes */
+	uint32_t options;       /* chip's characteristic bits */
+	uint32_t state;         /* current chip state */
+	uint32_t nxstate;       /* next expected state */
+	
+	uint32_t *op;           /* current operation, NULL operations isn't known yet  */
+	uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */
+	uint16_t npstates;      /* number of previous states saved */
+	uint16_t stateidx;      /* current state index */
+
+	/* The simulated NAND flash image */
+	union flash_media {
+		u_char *byte;
+		uint16_t    *word;
+	} mem;
+
+	/* Internal buffer of page + OOB size bytes */
+	union internal_buffer {
+		u_char *byte;    /* for byte access */
+		uint16_t *word;  /* for 16-bit word access */
+	} buf;
+
+	/* NAND flash "geometry" */
+	struct nandsin_geometry {
+		uint32_t totsz;     /* total flash size, bytes */
+		uint32_t secsz;     /* flash sector (erase block) size, bytes */
+		uint pgsz;          /* NAND flash page size, bytes */
+		uint oobsz;         /* page OOB area size, bytes */
+		uint32_t totszoob;  /* total flash size including OOB, bytes */
+		uint pgszoob;       /* page size including OOB , bytes*/
+		uint secszoob;      /* sector size including OOB, bytes */
+		uint pgnum;         /* total number of pages */
+		uint pgsec;         /* number of pages per sector */
+		uint secshift;      /* bits number in sector size */
+		uint pgshift;       /* bits number in page size */
+		uint oobshift;      /* bits number in OOB size */
+		uint pgaddrbytes;   /* bytes per page address */
+		uint secaddrbytes;  /* bytes per sector address */
+		uint idbytes;       /* the number ID bytes that this chip outputs */
+	} geom;
+
+	/* NAND flash internal registers */
+	struct nandsim_regs {
+		unsigned command; /* the command register */
+		u_char   status;  /* the status register */
+		uint     row;     /* the page number */
+		uint     column;  /* the offset within page */
+		uint     count;   /* internal counter */
+		uint     num;     /* number of bytes which must be processed */
+		uint     off;     /* fixed page offset */
+	} regs;
+
+	/* NAND flash lines state */
+        struct ns_lines_status {
+                int ce;  /* chip Enable */
+                int cle; /* command Latch Enable */
+                int ale; /* address Latch Enable */
+                int wp;  /* write Protect */
+        } lines;
+};
+
+/*
+ * Operations array. To perform any operation the simulator must pass
+ * through the correspondent states chain.
+ */
+static struct nandsim_operations {
+	uint32_t reqopts;  /* options which are required to perform the operation */
+	uint32_t states[NS_OPER_STATES]; /* operation's states */
+} ops[NS_OPER_NUM] = {
+	/* Read page + OOB from the beginning */
+	{OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY,
+			STATE_DATAOUT, STATE_READY}},
+	/* Read page + OOB from the second half */
+	{OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY,
+			STATE_DATAOUT, STATE_READY}},
+	/* Read OOB */
+	{OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY,
+			STATE_DATAOUT, STATE_READY}},
+	/* Programm page starting from the beginning */
+	{OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN,
+			STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+	/* Programm page starting from the beginning */
+	{OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE,
+			      STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+	/* Programm page starting from the second half */
+	{OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE,
+			      STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+	/* Programm OOB */
+	{OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE,
+			      STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+	/* Erase sector */
+	{OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}},
+	/* Read status */
+	{OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}},
+	/* Read multi-plane status */
+	{OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}},
+	/* Read ID */
+	{OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}},
+	/* Large page devices read page */
+	{OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY,
+			       STATE_DATAOUT, STATE_READY}}
+};
+
+/* MTD structure for NAND controller */
+static struct mtd_info *nsmtd;
+
+static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
+
+/*
+ * Initialize the nandsim structure.
+ *
+ * RETURNS: 0 if success, -ERRNO if failure.
+ */
+static int
+init_nandsim(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+	struct nandsim   *ns   = (struct nandsim *)(chip->priv);
+	int i;
+
+	if (NS_IS_INITIALIZED(ns)) {
+		NS_ERR("init_nandsim: nandsim is already initialized\n");
+		return -EIO;
+	}
+
+	/* Force mtd to not do delays */
+	chip->chip_delay = 0;
+
+	/* Initialize the NAND flash parameters */
+	ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
+	ns->geom.totsz    = mtd->size;
+	ns->geom.pgsz     = mtd->oobblock;
+	ns->geom.oobsz    = mtd->oobsize;
+	ns->geom.secsz    = mtd->erasesize;
+	ns->geom.pgszoob  = ns->geom.pgsz + ns->geom.oobsz;
+	ns->geom.pgnum    = ns->geom.totsz / ns->geom.pgsz;
+	ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz;
+	ns->geom.secshift = ffs(ns->geom.secsz) - 1;
+	ns->geom.pgshift  = chip->page_shift;
+	ns->geom.oobshift = ffs(ns->geom.oobsz) - 1;
+	ns->geom.pgsec    = ns->geom.secsz / ns->geom.pgsz;
+	ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
+	ns->options = 0;
+
+	if (ns->geom.pgsz == 256) {
+		ns->options |= OPT_PAGE256;
+	}
+	else if (ns->geom.pgsz == 512) {
+		ns->options |= (OPT_PAGE512 | OPT_AUTOINCR);
+		if (ns->busw == 8)
+			ns->options |= OPT_PAGE512_8BIT;
+	} else if (ns->geom.pgsz == 2048) {
+		ns->options |= OPT_PAGE2048;
+	} else {
+		NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz);
+		return -EIO;
+	}
+
+	if (ns->options & OPT_SMALLPAGE) {
+		if (ns->geom.totsz < (64 << 20)) {
+			ns->geom.pgaddrbytes  = 3;
+			ns->geom.secaddrbytes = 2;
+		} else {
+			ns->geom.pgaddrbytes  = 4;
+			ns->geom.secaddrbytes = 3;
+		}
+	} else {
+		if (ns->geom.totsz <= (128 << 20)) {
+			ns->geom.pgaddrbytes  = 4;
+			ns->geom.secaddrbytes = 2;
+		} else {
+			ns->geom.pgaddrbytes  = 5;
+			ns->geom.secaddrbytes = 3;
+		}
+	}
+	
+	/* Detect how many ID bytes the NAND chip outputs */
+        for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+                if (second_id_byte != nand_flash_ids[i].id)
+                        continue;
+		if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR))
+			ns->options |= OPT_AUTOINCR;
+	}
+
+	if (ns->busw == 16)
+		NS_WARN("16-bit flashes support wasn't tested\n");
+
+	printk("flash size: %u MiB\n",          ns->geom.totsz >> 20);
+	printk("page size: %u bytes\n",         ns->geom.pgsz);
+	printk("OOB area size: %u bytes\n",     ns->geom.oobsz);
+	printk("sector size: %u KiB\n",         ns->geom.secsz >> 10);
+	printk("pages number: %u\n",            ns->geom.pgnum);
+	printk("pages per sector: %u\n",        ns->geom.pgsec);
+	printk("bus width: %u\n",               ns->busw);
+	printk("bits in sector size: %u\n",     ns->geom.secshift);
+	printk("bits in page size: %u\n",       ns->geom.pgshift);
+	printk("bits in OOB size: %u\n",        ns->geom.oobshift);
+	printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10);
+	printk("page address bytes: %u\n",      ns->geom.pgaddrbytes);
+	printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);
+	printk("options: %#x\n",                ns->options);
+
+	/* Map / allocate and initialize the flash image */
+#ifdef CONFIG_NS_ABS_POS
+	ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob);
+	if (!ns->mem.byte) {
+		NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", 
+			(void *)CONFIG_NS_ABS_POS);
+		return -ENOMEM;
+	}
+#else
+	ns->mem.byte = vmalloc(ns->geom.totszoob);
+	if (!ns->mem.byte) {
+		NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n",
+			ns->geom.totszoob);
+		return -ENOMEM;
+	}
+	memset(ns->mem.byte, 0xFF, ns->geom.totszoob);
+#endif
+
+	/* Allocate / initialize the internal buffer */
+	ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+	if (!ns->buf.byte) {
+		NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
+			ns->geom.pgszoob);
+		goto error;
+	}
+	memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
+
+	/* Fill the partition_info structure */
+	ns->part.name   = "NAND simulator partition";
+	ns->part.offset = 0;
+	ns->part.size   = ns->geom.totsz;
+
+	return 0;
+
+error:
+#ifdef CONFIG_NS_ABS_POS
+	iounmap(ns->mem.byte);
+#else
+	vfree(ns->mem.byte);
+#endif
+
+	return -ENOMEM;
+}
+
+/*
+ * Free the nandsim structure.
+ */
+static void
+free_nandsim(struct nandsim *ns)
+{
+	kfree(ns->buf.byte);
+
+#ifdef CONFIG_NS_ABS_POS
+	iounmap(ns->mem.byte);
+#else
+	vfree(ns->mem.byte);
+#endif
+
+	return;
+}
+
+/*
+ * Returns the string representation of 'state' state.
+ */
+static char *
+get_state_name(uint32_t state)
+{
+	switch (NS_STATE(state)) {
+		case STATE_CMD_READ0:
+			return "STATE_CMD_READ0";
+		case STATE_CMD_READ1:
+			return "STATE_CMD_READ1";
+		case STATE_CMD_PAGEPROG:
+			return "STATE_CMD_PAGEPROG";
+		case STATE_CMD_READOOB:
+			return "STATE_CMD_READOOB";
+		case STATE_CMD_READSTART:
+			return "STATE_CMD_READSTART";
+		case STATE_CMD_ERASE1:
+			return "STATE_CMD_ERASE1";
+		case STATE_CMD_STATUS:
+			return "STATE_CMD_STATUS";
+		case STATE_CMD_STATUS_M:
+			return "STATE_CMD_STATUS_M";
+		case STATE_CMD_SEQIN:
+			return "STATE_CMD_SEQIN";
+		case STATE_CMD_READID:
+			return "STATE_CMD_READID";
+		case STATE_CMD_ERASE2:
+			return "STATE_CMD_ERASE2";
+		case STATE_CMD_RESET:
+			return "STATE_CMD_RESET";
+		case STATE_ADDR_PAGE:
+			return "STATE_ADDR_PAGE";
+		case STATE_ADDR_SEC:
+			return "STATE_ADDR_SEC";
+		case STATE_ADDR_ZERO:
+			return "STATE_ADDR_ZERO";
+		case STATE_DATAIN:
+			return "STATE_DATAIN";
+		case STATE_DATAOUT:
+			return "STATE_DATAOUT";
+		case STATE_DATAOUT_ID:
+			return "STATE_DATAOUT_ID";
+		case STATE_DATAOUT_STATUS:
+			return "STATE_DATAOUT_STATUS";
+		case STATE_DATAOUT_STATUS_M:
+			return "STATE_DATAOUT_STATUS_M";
+		case STATE_READY:
+			return "STATE_READY";
+		case STATE_UNKNOWN:
+			return "STATE_UNKNOWN";
+	}
+
+	NS_ERR("get_state_name: unknown state, BUG\n");
+	return NULL;
+}
+
+/*
+ * Check if command is valid.
+ *
+ * RETURNS: 1 if wrong command, 0 if right.
+ */
+static int
+check_command(int cmd)
+{
+	switch (cmd) {
+		
+	case NAND_CMD_READ0:
+	case NAND_CMD_READSTART:
+	case NAND_CMD_PAGEPROG:
+	case NAND_CMD_READOOB:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_STATUS:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_READID:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_RESET:
+	case NAND_CMD_READ1:
+		return 0;
+		
+	case NAND_CMD_STATUS_MULTI:
+	default:
+		return 1;
+	}
+}
+
+/*
+ * Returns state after command is accepted by command number.
+ */
+static uint32_t
+get_state_by_command(unsigned command)
+{
+	switch (command) {
+		case NAND_CMD_READ0:
+			return STATE_CMD_READ0;
+		case NAND_CMD_READ1:
+			return STATE_CMD_READ1;
+		case NAND_CMD_PAGEPROG:
+			return STATE_CMD_PAGEPROG;
+		case NAND_CMD_READSTART:
+			return STATE_CMD_READSTART;
+		case NAND_CMD_READOOB:
+			return STATE_CMD_READOOB;
+		case NAND_CMD_ERASE1:
+			return STATE_CMD_ERASE1;
+		case NAND_CMD_STATUS:
+			return STATE_CMD_STATUS;
+		case NAND_CMD_STATUS_MULTI:
+			return STATE_CMD_STATUS_M;
+		case NAND_CMD_SEQIN:
+			return STATE_CMD_SEQIN;
+		case NAND_CMD_READID:
+			return STATE_CMD_READID;
+		case NAND_CMD_ERASE2:
+			return STATE_CMD_ERASE2;
+		case NAND_CMD_RESET:
+			return STATE_CMD_RESET;
+	}
+
+	NS_ERR("get_state_by_command: unknown command, BUG\n");
+	return 0;
+}
+
+/*
+ * Move an address byte to the correspondent internal register.
+ */
+static inline void
+accept_addr_byte(struct nandsim *ns, u_char bt)
+{
+	uint byte = (uint)bt;
+	
+	if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes))
+		ns->regs.column |= (byte << 8 * ns->regs.count);
+	else {
+		ns->regs.row |= (byte << 8 * (ns->regs.count -
+						ns->geom.pgaddrbytes +
+						ns->geom.secaddrbytes));
+	}
+
+	return;
+}
+		
+/*
+ * Switch to STATE_READY state.
+ */
+static inline void 
+switch_to_ready_state(struct nandsim *ns, u_char status)
+{
+	NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
+
+	ns->state       = STATE_READY;
+	ns->nxstate     = STATE_UNKNOWN;
+	ns->op          = NULL;
+	ns->npstates    = 0;
+	ns->stateidx    = 0;
+	ns->regs.num    = 0;
+	ns->regs.count  = 0;
+	ns->regs.off    = 0;
+	ns->regs.row    = 0;
+	ns->regs.column = 0;
+	ns->regs.status = status;
+}
+
+/*
+ * If the operation isn't known yet, try to find it in the global array
+ * of supported operations.
+ *
+ * Operation can be unknown because of the following.
+ *   1. New command was accepted and this is the firs call to find the
+ *      correspondent states chain. In this case ns->npstates = 0;
+ *   2. There is several operations which begin with the same command(s)
+ *      (for example program from the second half and read from the
+ *      second half operations both begin with the READ1 command). In this
+ *      case the ns->pstates[] array contains previous states.
+ * 
+ * Thus, the function tries to find operation containing the following
+ * states (if the 'flag' parameter is 0):
+ *    ns->pstates[0], ... ns->pstates[ns->npstates], ns->state
+ *
+ * If (one and only one) matching operation is found, it is accepted (
+ * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is
+ * zeroed).
+ * 
+ * If there are several maches, the current state is pushed to the
+ * ns->pstates.
+ *
+ * The operation can be unknown only while commands are input to the chip.
+ * As soon as address command is accepted, the operation must be known.
+ * In such situation the function is called with 'flag' != 0, and the
+ * operation is searched using the following pattern:
+ *     ns->pstates[0], ... ns->pstates[ns->npstates], <address input>
+ * 
+ * It is supposed that this pattern must either match one operation on
+ * none. There can't be ambiguity in that case.
+ *
+ * If no matches found, the functions does the following:
+ *   1. if there are saved states present, try to ignore them and search
+ *      again only using the last command. If nothing was found, switch
+ *      to the STATE_READY state.
+ *   2. if there are no saved states, switch to the STATE_READY state.
+ *
+ * RETURNS: -2 - no matched operations found.
+ *          -1 - several matches.
+ *           0 - operation is found.
+ */
+static int
+find_operation(struct nandsim *ns, uint32_t flag)
+{
+	int opsfound = 0;
+	int i, j, idx = 0;
+	
+	for (i = 0; i < NS_OPER_NUM; i++) {
+
+		int found = 1;
+	
+		if (!(ns->options & ops[i].reqopts))
+			/* Ignore operations we can't perform */
+			continue;
+			
+		if (flag) {
+			if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK))
+				continue;
+		} else {
+			if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates]))
+				continue;
+		}
+
+		for (j = 0; j < ns->npstates; j++) 
+			if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j])
+				&& (ns->options & ops[idx].reqopts)) {
+				found = 0;
+				break;
+			}
+
+		if (found) {
+			idx = i;
+			opsfound += 1;
+		}
+	}
+
+	if (opsfound == 1) {
+		/* Exact match */
+		ns->op = &ops[idx].states[0];
+		if (flag) {
+			/* 
+			 * In this case the find_operation function was
+			 * called when address has just began input. But it isn't
+			 * yet fully input and the current state must
+			 * not be one of STATE_ADDR_*, but the STATE_ADDR_*
+			 * state must be the next state (ns->nxstate).
+			 */
+			ns->stateidx = ns->npstates - 1;
+		} else {
+			ns->stateidx = ns->npstates;
+		}
+		ns->npstates = 0;
+		ns->state = ns->op[ns->stateidx];
+		ns->nxstate = ns->op[ns->stateidx + 1];
+		NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n",
+				idx, get_state_name(ns->state), get_state_name(ns->nxstate));
+		return 0;
+	}
+	
+	if (opsfound == 0) {
+		/* Nothing was found. Try to ignore previous commands (if any) and search again */
+		if (ns->npstates != 0) {
+			NS_DBG("find_operation: no operation found, try again with state %s\n",
+					get_state_name(ns->state));
+			ns->npstates = 0;
+			return find_operation(ns, 0);
+
+		}
+		NS_DBG("find_operation: no operations found\n");
+		switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+		return -2;
+	}
+	
+	if (flag) {
+		/* This shouldn't happen */
+		NS_DBG("find_operation: BUG, operation must be known if address is input\n");
+		return -2;
+	}
+	
+	NS_DBG("find_operation: there is still ambiguity\n");
+
+	ns->pstates[ns->npstates++] = ns->state;
+
+	return -1;
+}
+
+/*
+ * If state has any action bit, perform this action.
+ *
+ * RETURNS: 0 if success, -1 if error.
+ */
+static int
+do_state_action(struct nandsim *ns, uint32_t action)
+{
+	int i, num;
+	int busdiv = ns->busw == 8 ? 1 : 2;
+
+	action &= ACTION_MASK;
+	
+	/* Check that page address input is correct */
+	if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) {
+		NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row);
+		return -1;
+	}
+
+	switch (action) {
+
+	case ACTION_CPY:
+		/*
+		 * Copy page data to the internal buffer.
+		 */
+
+		/* Column shouldn't be very large */
+		if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) {
+			NS_ERR("do_state_action: column number is too large\n");
+			break;
+		}
+		num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+		memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num);
+
+		NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
+			num, NS_RAW_OFFSET(ns) + ns->regs.off);
+		
+		if (ns->regs.off == 0)
+			NS_LOG("read page %d\n", ns->regs.row);
+		else if (ns->regs.off < ns->geom.pgsz)
+			NS_LOG("read page %d (second half)\n", ns->regs.row);
+		else
+			NS_LOG("read OOB of page %d\n", ns->regs.row);
+		
+		NS_UDELAY(access_delay);
+		NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv);
+
+		break;
+
+	case ACTION_SECERASE:
+		/*
+		 * Erase sector.
+		 */
+		
+		if (ns->lines.wp) {
+			NS_ERR("do_state_action: device is write-protected, ignore sector erase\n");
+			return -1;
+		}
+		
+		if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec
+			|| (ns->regs.row & ~(ns->geom.secsz - 1))) {
+			NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row);
+			return -1;
+		}
+		
+		ns->regs.row = (ns->regs.row <<
+				8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
+		ns->regs.column = 0;
+		
+		NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
+				ns->regs.row, NS_RAW_OFFSET(ns));
+		NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
+
+		memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob);
+		
+		NS_MDELAY(erase_delay);
+		
+		break;
+
+	case ACTION_PRGPAGE:
+		/*
+		 * Programm page - move internal buffer data to the page.
+		 */
+
+		if (ns->lines.wp) {
+			NS_WARN("do_state_action: device is write-protected, programm\n");
+			return -1;
+		}
+
+		num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+		if (num != ns->regs.count) {
+			NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
+					ns->regs.count, num);
+			return -1;
+		}
+
+		for (i = 0; i < num; i++)
+			ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i];
+
+		NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
+			num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
+		NS_LOG("programm page %d\n", ns->regs.row);
+		
+		NS_UDELAY(programm_delay);
+		NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
+		
+		break;
+	
+	case ACTION_ZEROOFF:
+		NS_DBG("do_state_action: set internal offset to 0\n");
+		ns->regs.off = 0;
+		break;
+
+	case ACTION_HALFOFF:
+		if (!(ns->options & OPT_PAGE512_8BIT)) {
+			NS_ERR("do_state_action: BUG! can't skip half of page for non-512"
+				"byte page size 8x chips\n");
+			return -1;
+		}
+		NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2);
+		ns->regs.off = ns->geom.pgsz/2;
+		break;
+
+	case ACTION_OOBOFF:
+		NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz);
+		ns->regs.off = ns->geom.pgsz;
+		break;
+		
+	default:
+		NS_DBG("do_state_action: BUG! unknown action\n");
+	}
+
+	return 0;
+}
+
+/*
+ * Switch simulator's state.
+ */
+static void
+switch_state(struct nandsim *ns)
+{
+	if (ns->op) {
+		/*
+		 * The current operation have already been identified.
+		 * Just follow the states chain.
+		 */
+		
+		ns->stateidx += 1;
+		ns->state = ns->nxstate;
+		ns->nxstate = ns->op[ns->stateidx + 1];
+
+		NS_DBG("switch_state: operation is known, switch to the next state, "
+			"state: %s, nxstate: %s\n",
+			get_state_name(ns->state), get_state_name(ns->nxstate));
+
+		/* See, whether we need to do some action */
+		if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+			return;
+		}
+		
+	} else {
+		/*
+		 * We don't yet know which operation we perform.
+		 * Try to identify it.
+		 */
+
+		/*  
+		 *  The only event causing the switch_state function to
+		 *  be called with yet unknown operation is new command.
+		 */
+		ns->state = get_state_by_command(ns->regs.command);
+
+		NS_DBG("switch_state: operation is unknown, try to find it\n");
+
+		if (find_operation(ns, 0) != 0)
+			return;
+
+		if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+			return;
+		}
+	}
+
+	/* For 16x devices column means the page offset in words */
+	if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) {
+		NS_DBG("switch_state: double the column number for 16x device\n");
+		ns->regs.column <<= 1;
+	}
+
+	if (NS_STATE(ns->nxstate) == STATE_READY) {
+		/*
+		 * The current state is the last. Return to STATE_READY
+		 */
+
+		u_char status = NS_STATUS_OK(ns);
+		
+		/* In case of data states, see if all bytes were input/output */
+		if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK))
+			&& ns->regs.count != ns->regs.num) {
+			NS_WARN("switch_state: not all bytes were processed, %d left\n",
+					ns->regs.num - ns->regs.count);
+			status = NS_STATUS_FAILED(ns);
+		}
+				
+		NS_DBG("switch_state: operation complete, switch to STATE_READY state\n");
+
+		switch_to_ready_state(ns, status);
+
+		return;
+	} else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) {
+		/* 
+		 * If the next state is data input/output, switch to it now
+		 */
+		
+		ns->state      = ns->nxstate;
+		ns->nxstate    = ns->op[++ns->stateidx + 1];
+		ns->regs.num   = ns->regs.count = 0;
+
+		NS_DBG("switch_state: the next state is data I/O, switch, "
+			"state: %s, nxstate: %s\n",
+			get_state_name(ns->state), get_state_name(ns->nxstate));
+
+		/*
+		 * Set the internal register to the count of bytes which
+		 * are expected to be input or output
+		 */
+		switch (NS_STATE(ns->state)) {
+			case STATE_DATAIN:
+			case STATE_DATAOUT:
+				ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+				break;
+				
+			case STATE_DATAOUT_ID:
+				ns->regs.num = ns->geom.idbytes;
+				break;
+				
+			case STATE_DATAOUT_STATUS:
+			case STATE_DATAOUT_STATUS_M:
+				ns->regs.count = ns->regs.num = 0;
+				break;
+				
+			default:
+				NS_ERR("switch_state: BUG! unknown data state\n");
+		}
+
+	} else if (ns->nxstate & STATE_ADDR_MASK) {
+		/*
+		 * If the next state is address input, set the internal
+		 * register to the number of expected address bytes
+		 */
+
+		ns->regs.count = 0;
+		
+		switch (NS_STATE(ns->nxstate)) {
+			case STATE_ADDR_PAGE:
+				ns->regs.num = ns->geom.pgaddrbytes;
+		
+				break;
+			case STATE_ADDR_SEC:
+				ns->regs.num = ns->geom.secaddrbytes;
+				break;
+	
+			case STATE_ADDR_ZERO:
+				ns->regs.num = 1;
+				break;
+
+			default:
+				NS_ERR("switch_state: BUG! unknown address state\n");
+		}
+	} else {
+		/* 
+		 * Just reset internal counters.
+		 */
+
+		ns->regs.num = 0;
+		ns->regs.count = 0;
+	}
+}
+
+static void
+ns_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+	struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+
+	switch (cmd) {
+
+	/* set CLE line high */
+	case NAND_CTL_SETCLE:
+		NS_DBG("ns_hwcontrol: start command latch cycles\n");
+		ns->lines.cle  = 1;
+		break;
+
+	/* set CLE line low */
+	case NAND_CTL_CLRCLE:
+		NS_DBG("ns_hwcontrol: stop command latch cycles\n");
+		ns->lines.cle  = 0;
+		break;
+
+	/* set ALE line high */
+	case NAND_CTL_SETALE:
+		NS_DBG("ns_hwcontrol: start address latch cycles\n");
+		ns->lines.ale   = 1;
+		break;
+
+	/* set ALE line low */
+	case NAND_CTL_CLRALE:
+		NS_DBG("ns_hwcontrol: stop address latch cycles\n");
+		ns->lines.ale  = 0;
+		break;
+
+	/* set WP line high */
+	case NAND_CTL_SETWP:
+		NS_DBG("ns_hwcontrol: enable write protection\n");
+		ns->lines.wp = 1;
+		break;
+
+	/* set WP line low */
+	case NAND_CTL_CLRWP:
+		NS_DBG("ns_hwcontrol: disable write protection\n");
+		ns->lines.wp = 0;
+		break;
+
+	/* set CE line low */
+	case NAND_CTL_SETNCE:
+		NS_DBG("ns_hwcontrol: enable chip\n");
+		ns->lines.ce = 1;
+		break;
+
+	/* set CE line high */
+	case NAND_CTL_CLRNCE:
+		NS_DBG("ns_hwcontrol: disable chip\n");
+		ns->lines.ce = 0;
+		break;
+
+	default:
+		NS_ERR("hwcontrol: unknown command\n");
+        }
+
+	return;
+}
+
+static u_char
+ns_nand_read_byte(struct mtd_info *mtd)
+{
+        struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+	u_char outb = 0x00;
+
+	/* Sanity and correctness checks */
+	if (!ns->lines.ce) {
+		NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb);
+		return outb;
+	}
+	if (ns->lines.ale || ns->lines.cle) {
+		NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb);
+		return outb;
+	}
+	if (!(ns->state & STATE_DATAOUT_MASK)) {
+		NS_WARN("read_byte: unexpected data output cycle, state is %s "
+			"return %#x\n", get_state_name(ns->state), (uint)outb);
+		return outb;
+	}
+
+	/* Status register may be read as many times as it is wanted */
+	if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) {
+		NS_DBG("read_byte: return %#x status\n", ns->regs.status);
+		return ns->regs.status;
+	}
+
+	/* Check if there is any data in the internal buffer which may be read */
+	if (ns->regs.count == ns->regs.num) {
+		NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb);
+		return outb;
+	}
+
+	switch (NS_STATE(ns->state)) {
+		case STATE_DATAOUT:
+			if (ns->busw == 8) {
+				outb = ns->buf.byte[ns->regs.count];
+				ns->regs.count += 1;
+			} else {
+				outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]);
+				ns->regs.count += 2;
+			}
+			break;
+		case STATE_DATAOUT_ID:
+			NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num);
+			outb = ns->ids[ns->regs.count];
+			ns->regs.count += 1;
+			break;
+		default:
+			BUG();
+	}
+	
+	if (ns->regs.count == ns->regs.num) {
+		NS_DBG("read_byte: all bytes were read\n");
+
+		/*
+		 * The OPT_AUTOINCR allows to read next conseqitive pages without
+		 * new read operation cycle.
+		 */
+		if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
+			ns->regs.count = 0;
+			if (ns->regs.row + 1 < ns->geom.pgnum)
+				ns->regs.row += 1;
+			NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row);
+			do_state_action(ns, ACTION_CPY);
+		}
+		else if (NS_STATE(ns->nxstate) == STATE_READY)
+			switch_state(ns);
+		
+	}
+	
+	return outb;
+}
+
+static void
+ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+        struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+	
+	/* Sanity and correctness checks */
+	if (!ns->lines.ce) {
+		NS_ERR("write_byte: chip is disabled, ignore write\n");
+		return;
+	}
+	if (ns->lines.ale && ns->lines.cle) {
+		NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n");
+		return;
+	}
+			
+	if (ns->lines.cle == 1) {
+		/*
+		 * The byte written is a command.
+		 */
+
+		if (byte == NAND_CMD_RESET) {
+			NS_LOG("reset chip\n");
+			switch_to_ready_state(ns, NS_STATUS_OK(ns));
+			return;
+		}
+
+		/* 
+		 * Chip might still be in STATE_DATAOUT
+		 * (if OPT_AUTOINCR feature is supported), STATE_DATAOUT_STATUS or
+		 * STATE_DATAOUT_STATUS_M state. If so, switch state.
+		 */
+		if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS
+			|| NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M
+			|| ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT))
+			switch_state(ns);
+
+		/* Check if chip is expecting command */
+		if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) {
+			/*
+			 * We are in situation when something else (not command)
+			 * was expected but command was input. In this case ignore
+			 * previous command(s)/state(s) and accept the last one.
+			 */
+			NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, "
+				"ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
+			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+		}
+		
+		/* Check that the command byte is correct */
+		if (check_command(byte)) {
+			NS_ERR("write_byte: unknown command %#x\n", (uint)byte);
+			return;
+		}
+		
+		NS_DBG("command byte corresponding to %s state accepted\n",
+			get_state_name(get_state_by_command(byte)));
+		ns->regs.command = byte;
+		switch_state(ns);
+
+	} else if (ns->lines.ale == 1) {
+		/*
+		 * The byte written is an address.
+		 */
+
+		if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) {
+
+			NS_DBG("write_byte: operation isn't known yet, identify it\n");
+
+			if (find_operation(ns, 1) < 0)
+				return;
+			
+			if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+				switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+				return;
+			}
+				
+			ns->regs.count = 0;
+			switch (NS_STATE(ns->nxstate)) {
+				case STATE_ADDR_PAGE:
+					ns->regs.num = ns->geom.pgaddrbytes;
+					break;
+				case STATE_ADDR_SEC:
+					ns->regs.num = ns->geom.secaddrbytes;
+					break;
+				case STATE_ADDR_ZERO:
+					ns->regs.num = 1;
+					break;
+				default:
+					BUG();
+			}
+		}
+
+		/* Check that chip is expecting address */
+		if (!(ns->nxstate & STATE_ADDR_MASK)) {
+			NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, "
+				"switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate));
+			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+			return;
+		}
+		
+		/* Check if this is expected byte */
+		if (ns->regs.count == ns->regs.num) {
+			NS_ERR("write_byte: no more address bytes expected\n");
+			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+			return;
+		}
+
+		accept_addr_byte(ns, byte);
+
+		ns->regs.count += 1;
+
+		NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n",
+				(uint)byte, ns->regs.count, ns->regs.num);
+
+		if (ns->regs.count == ns->regs.num) {
+			NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column);
+			switch_state(ns);
+		}
+		
+	} else {
+		/*
+		 * The byte written is an input data.
+		 */
+		
+		/* Check that chip is expecting data input */
+		if (!(ns->state & STATE_DATAIN_MASK)) {
+			NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, "
+				"switch to %s\n", (uint)byte,
+				get_state_name(ns->state), get_state_name(STATE_READY));
+			switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+			return;
+		}
+
+		/* Check if this is expected byte */
+		if (ns->regs.count == ns->regs.num) {
+			NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n",
+					ns->regs.num);
+			return;
+		}
+
+		if (ns->busw == 8) {
+			ns->buf.byte[ns->regs.count] = byte;
+			ns->regs.count += 1;
+		} else {
+			ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte);
+			ns->regs.count += 2;
+		}
+	}
+
+	return;
+}
+
+static int
+ns_device_ready(struct mtd_info *mtd)
+{
+	NS_DBG("device_ready\n");
+	return 1;
+}
+
+static uint16_t
+ns_nand_read_word(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+
+	NS_DBG("read_word\n");
+	
+	return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
+}
+
+static void
+ns_nand_write_word(struct mtd_info *mtd, uint16_t word)
+{
+	struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+	
+	NS_DBG("write_word\n");
+	
+	chip->write_byte(mtd, word & 0xFF);
+	chip->write_byte(mtd, word >> 8);
+}
+
+static void 
+ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+        struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+
+	/* Check that chip is expecting data input */
+	if (!(ns->state & STATE_DATAIN_MASK)) {
+		NS_ERR("write_buf: data input isn't expected, state is %s, "
+			"switch to STATE_READY\n", get_state_name(ns->state));
+		switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+		return;
+	}
+
+	/* Check if these are expected bytes */
+	if (ns->regs.count + len > ns->regs.num) {
+		NS_ERR("write_buf: too many input bytes\n");
+		switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+		return;
+	}
+
+	memcpy(ns->buf.byte + ns->regs.count, buf, len);
+	ns->regs.count += len;
+	
+	if (ns->regs.count == ns->regs.num) {
+		NS_DBG("write_buf: %d bytes were written\n", ns->regs.count);
+	}
+}
+
+static void 
+ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+        struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+
+	/* Sanity and correctness checks */
+	if (!ns->lines.ce) {
+		NS_ERR("read_buf: chip is disabled\n");
+		return;
+	}
+	if (ns->lines.ale || ns->lines.cle) {
+		NS_ERR("read_buf: ALE or CLE pin is high\n");
+		return;
+	}
+	if (!(ns->state & STATE_DATAOUT_MASK)) {
+		NS_WARN("read_buf: unexpected data output cycle, current state is %s\n",
+			get_state_name(ns->state));
+		return;
+	}
+
+	if (NS_STATE(ns->state) != STATE_DATAOUT) {
+		int i;
+
+		for (i = 0; i < len; i++)
+			buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd);
+
+		return;
+	}
+
+	/* Check if these are expected bytes */
+	if (ns->regs.count + len > ns->regs.num) {
+		NS_ERR("read_buf: too many bytes to read\n");
+		switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+		return;
+	}
+
+	memcpy(buf, ns->buf.byte + ns->regs.count, len);
+	ns->regs.count += len;
+	
+	if (ns->regs.count == ns->regs.num) {
+		if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
+			ns->regs.count = 0;
+			if (ns->regs.row + 1 < ns->geom.pgnum)
+				ns->regs.row += 1;
+			NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row);
+			do_state_action(ns, ACTION_CPY);
+		}
+		else if (NS_STATE(ns->nxstate) == STATE_READY)
+			switch_state(ns);
+	}
+	
+	return;
+}
+
+static int 
+ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
+
+	if (!memcmp(buf, &ns_verify_buf[0], len)) {
+		NS_DBG("verify_buf: the buffer is OK\n");
+		return 0;
+	} else {
+		NS_DBG("verify_buf: the buffer is wrong\n");
+		return -EFAULT;
+	}
+}
+
+/*
+ * Module initialization function
+ */
+int __init ns_init_module(void)
+{
+	struct nand_chip *chip;
+	struct nandsim *nand;
+	int retval = -ENOMEM;
+
+	if (bus_width != 8 && bus_width != 16) {
+		NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
+		return -EINVAL;
+	}
+	
+	/* Allocate and initialize mtd_info, nand_chip and nandsim structures */
+	nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
+				+ sizeof(struct nandsim), GFP_KERNEL);
+	if (!nsmtd) {
+		NS_ERR("unable to allocate core structures.\n");
+		return -ENOMEM;
+	}
+	memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) +
+			sizeof(struct nandsim));
+	chip        = (struct nand_chip *)(nsmtd + 1);
+        nsmtd->priv = (void *)chip;
+	nand        = (struct nandsim *)(chip + 1);
+	chip->priv  = (void *)nand;	
+
+	/*
+	 * Register simulator's callbacks.
+	 */
+	chip->hwcontrol  = ns_hwcontrol;
+	chip->read_byte  = ns_nand_read_byte;
+	chip->dev_ready  = ns_device_ready;
+	chip->write_byte = ns_nand_write_byte;
+	chip->write_buf  = ns_nand_write_buf;
+	chip->read_buf   = ns_nand_read_buf;
+	chip->verify_buf = ns_nand_verify_buf;
+	chip->write_word = ns_nand_write_word;
+	chip->read_word  = ns_nand_read_word;
+	chip->eccmode    = NAND_ECC_SOFT;
+	chip->options   |= NAND_SKIP_BBTSCAN;
+
+	/* 
+	 * Perform minimum nandsim structure initialization to handle
+	 * the initial ID read command correctly 
+	 */
+	if (third_id_byte != 0xFF || fourth_id_byte != 0xFF)
+		nand->geom.idbytes = 4;
+	else
+		nand->geom.idbytes = 2;
+	nand->regs.status = NS_STATUS_OK(nand);
+	nand->nxstate = STATE_UNKNOWN;
+	nand->options |= OPT_PAGE256; /* temporary value */
+	nand->ids[0] = first_id_byte;
+	nand->ids[1] = second_id_byte;
+	nand->ids[2] = third_id_byte;
+	nand->ids[3] = fourth_id_byte;
+	if (bus_width == 16) {
+		nand->busw = 16;
+		chip->options |= NAND_BUSWIDTH_16;
+	}
+
+	if ((retval = nand_scan(nsmtd, 1)) != 0) {
+		NS_ERR("can't register NAND Simulator\n");
+		if (retval > 0)
+			retval = -ENXIO;
+		goto error;
+	}
+
+	if ((retval = init_nandsim(nsmtd)) != 0) {
+		NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
+		goto error;
+	}
+	
+	if ((retval = nand_default_bbt(nsmtd)) != 0) {
+		free_nandsim(nand);
+		goto error;
+	}
+
+	/* Register NAND as one big partition */
+	add_mtd_partitions(nsmtd, &nand->part, 1);
+
+        return 0;
+
+error:
+	kfree(nsmtd);
+
+	return retval;
+}
+
+module_init(ns_init_module);
+
+/*
+ * Module clean-up function
+ */
+static void __exit ns_cleanup_module(void)
+{
+	struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv);
+
+	free_nandsim(ns);    /* Free nandsim private resources */
+	nand_release(nsmtd); /* Unregisterd drived */
+	kfree(nsmtd);        /* Free other structures */
+}
+
+module_exit(ns_cleanup_module);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Artem B. Bityuckiy");
+MODULE_DESCRIPTION ("The NAND flash simulator");
+
diff -x asm -Naur linux-2.4.30-orig/drivers/net/au1000_eth.c linux-2.4.30/drivers/net/au1000_eth.c
--- linux-2.4.30-orig/drivers/net/au1000_eth.c	2005-05-12 17:57:26.000000000 +0200
+++ linux-2.4.30/drivers/net/au1000_eth.c	2006-12-11 18:18:37.000000000 +0100
@@ -58,6 +58,7 @@
 #include <asm/cpu.h>
 #include "au1000_eth.h"
 
+#define I3_PATCH
 
 #ifdef AU1000_ETH_DEBUG
 static int au1000_debug = 5;
@@ -97,6 +98,7 @@
 extern inline void str2eaddr(unsigned char *ea, unsigned char *str);
 extern inline unsigned char str2hexnum(unsigned char c);
 extern char * __init prom_getcmdline(void);
+extern char *prom_getenv(char *envname);
 
 /*
  * Theory of operation
@@ -139,12 +141,14 @@
  * code.
  */
 
+#if 0
 static char *phy_link[] = 
 	{"unknown", 
 	"10Base2", "10BaseT", 
 	"AUI",
 	"100BaseT", "100BaseTX", "100BaseFX"
 	};
+#endif
 
 int bcm_5201_init(struct net_device *dev, int phy_addr)
 {
@@ -595,6 +599,93 @@
 	return 0;
 }
 
+int ks8721_init(struct net_device *dev, int phy_addr)
+{
+//	s16 data;
+	
+	printk("ks8721m_init - phy = %d\n", phy_addr);
+
+#if 0
+	/* Stop auto-negotiation */
+	data = mdio_read(dev, phy_addr, MII_CONTROL);
+	mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
+
+	/* Set advertisement to 10/100 and Half/Full duplex
+	 * (full capabilities) */
+	data = mdio_read(dev, phy_addr, MII_ANADV);
+	data |= MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_T_FDX | MII_NWAY_T;
+	mdio_write(dev, phy_addr, MII_ANADV, data);
+	
+	/* Restart auto-negotiation */
+	data = mdio_read(dev, phy_addr, MII_CONTROL);
+	data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
+	mdio_write(dev, phy_addr, MII_CONTROL, data);
+#endif
+
+	if (au1000_debug > 4)
+	    dump_mii(dev, phy_addr);
+
+	return 0;
+}
+
+int ks8721_reset(struct net_device *dev, int phy_addr)
+{
+	s16 mii_control, timeout;
+	
+	printk("ks8995m_reset\n");
+	mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+	mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+	mdelay(1);
+	for (timeout = 100; timeout > 0; --timeout) {
+		mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+		if ((mii_control & MII_CNTL_RESET) == 0)
+			break;
+		mdelay(1);
+	}
+	if (mii_control & MII_CNTL_RESET) {
+		printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+		return -1;
+	}
+	return 0;
+}
+
+int ks8721_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+	u16 mii_data;
+	struct au1000_private *aup;
+
+	if (!dev) {
+		printk(KERN_ERR "ks8995m_status error: NULL dev\n");
+		return -1;
+	}
+	aup = (struct au1000_private *) dev->priv;
+
+	mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+	if (mii_data & MII_STAT_LINK) {
+		*link = 1;
+		mii_data = mdio_read(dev, aup->phy_addr, MII_PHY_CNTRL);
+		switch((mii_data >> 2) & 0x7) {
+		case 6: /* 100 Full Duplex */
+		    *speed = IF_PORT_100BASEFX;
+		    dev->if_port = IF_PORT_100BASEFX;
+		    break;
+		case 2: /* 100 Half Duplex */
+		    *speed = IF_PORT_100BASETX;
+		    dev->if_port = IF_PORT_100BASETX;
+		    break;
+		default:
+		    *speed = IF_PORT_10BASET;
+		    dev->if_port = IF_PORT_10BASET;
+		}
+	}
+	else {
+		*link = 0;
+		*speed = 0;
+		dev->if_port = IF_PORT_UNKNOWN;
+	}
+	return 0;
+}
+
 #ifdef CONFIG_MIPS_BOSPORUS
 int stub_init(struct net_device *dev, int phy_addr)
 {
@@ -656,6 +747,12 @@
 	ks8995m_status,
 };
 
+struct phy_ops ks8721_ops = {
+	ks8721_init,
+	ks8721_reset,
+	ks8721_status,
+};
+
 #ifdef CONFIG_MIPS_BOSPORUS
 struct phy_ops stub_ops = {
 	stub_init,
@@ -679,6 +776,7 @@
 	{"LSI 80227 10/100 BaseT PHY",0x0016,0xf840, &lsi_80227_ops,0},
 	{"Intel LXT971A Dual Speed PHY",0x0013,0x78e2, &lxt971a_ops,0},
 	{"Kendin KS8995M 10/100 BaseT PHY",0x0022,0x1450, &ks8995m_ops,0},
+	{"Micrel KS8721b/BT 10/100 Base Tx/Fx PHY",0x0022,0x1619, &ks8721_ops,0},
 #ifdef CONFIG_MIPS_BOSPORUS
 	{"Stub", 0x1234, 0x5678, &stub_ops },
 #endif
@@ -1163,7 +1261,8 @@
 	struct au1000_private *aup = NULL;
 	struct net_device *dev = NULL;
 	db_dest_t *pDB, *pDBfree;
-	char *pmac, *argptr;
+	//	char *pmac;
+	char *argptr;
 	char ethaddr[6];
 	int i, err;
 
@@ -1209,6 +1308,17 @@
 		if (!get_ethernet_addr(ethaddr)) { 
 			memcpy(au1000_mac_addr, ethaddr, sizeof(dev->dev_addr));
 		} else {
+#ifdef CONFIG_MIPS_I3_MMB3
+                        argptr = prom_getenv("ethaddr");
+			if(!argptr) {
+				printk(KERN_INFO "%s: No mac address found\n", 
+						dev->name);
+			} else {
+				str2eaddr(ethaddr, argptr);
+				memcpy(au1000_mac_addr, ethaddr, 
+						sizeof(dev->dev_addr));
+			}
+#else
 			/* Check command line */
 			argptr = prom_getcmdline();
 			if ((pmac = strstr(argptr, "ethaddr=")) == NULL) {
@@ -1220,6 +1330,7 @@
 				memcpy(au1000_mac_addr, ethaddr, 
 						sizeof(dev->dev_addr));
 			}
+#endif
 		}
 			aup->enable = (volatile u32 *) 
 				((unsigned long)iflist[0].macen_addr);
@@ -1387,6 +1498,7 @@
 #endif
 	if (link && (dev->if_port == IF_PORT_100BASEFX)) {
 		control |= MAC_FULL_DUPLEX;
+		control &= ~MAC_DISABLE_RX_OWN; /* Added by I3 */
 	}
 	aup->mac->control = control;
 	aup->mac->vlan1_tag = 0x8100; /* activate vlan support */
@@ -1469,7 +1581,12 @@
 	}
 	netif_start_queue(dev);
 
-	if ((retval = request_irq(dev->irq, &au1000_interrupt, 0, 
+#ifdef I3_PATCH
+        // Request fast non-nested interrupt. // AndreasN
+	if ((retval = request_irq(dev->irq, &au1000_interrupt, SA_INTERRUPT,
+#else
+	if ((retval = request_irq(dev->irq, &au1000_interrupt, 0,
+#endif
 					dev->name, dev))) {
 		printk(KERN_ERR "%s: unable to get IRQ %d\n", 
 				dev->name, dev->irq);
diff -x asm -Naur linux-2.4.30-orig/drivers/net/au1000_eth.h linux-2.4.30/drivers/net/au1000_eth.h
--- linux-2.4.30-orig/drivers/net/au1000_eth.h	2005-05-12 17:57:26.000000000 +0200
+++ linux-2.4.30/drivers/net/au1000_eth.h	2006-12-11 18:18:37.000000000 +0100
@@ -140,6 +140,9 @@
 #define MII_AUX_F100     0x0004
 #define MII_AUX_ANEG     0x0008
 
+/* Micrel KS8721B/BT */
+#define MII_PHY_CNTRL 0x001f
+
 typedef struct mii_phy {
 	struct mii_phy * next;
 	struct mii_chip_info * chip_info;
diff -x asm -Naur linux-2.4.30-orig/drivers/sound/au1550_psc.c linux-2.4.30/drivers/sound/au1550_psc.c
--- linux-2.4.30-orig/drivers/sound/au1550_psc.c	2005-05-12 17:57:32.000000000 +0200
+++ linux-2.4.30/drivers/sound/au1550_psc.c	2006-12-11 18:18:37.000000000 +0100
@@ -56,6 +56,8 @@
 #include <asm/au1xxx_psc.h>
 #include <asm/au1xxx_dbdma.h>
 
+#define I3_PATCH
+
 #ifdef CONFIG_MIPS_PB1550
 #include <asm/pb1550.h>
 #endif
@@ -72,6 +74,10 @@
 #include <asm/db1200.h>
 #endif
 
+#ifdef CONFIG_MIPS_I3_MMB3
+#include <asm/mood300.h>
+#endif
+
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
 
 #define AU1550_MODULE_NAME "Au1550 psc audio"
@@ -88,6 +94,12 @@
  */
 #define NUM_DBDMA_DESCRIPTORS 4
 
+#ifdef I3_PATCH
+//#define DMA_RXTX_LOOP_TRANSFER_SIZE 48*1024+1  // i3 patch
+/* Bug fix for click sound on SPDIF PCM */
+#define DMA_RXTX_LOOP_TRANSFER_SIZE 48*1024
+#endif
+
 #define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
 #define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
 
@@ -98,6 +110,10 @@
 MODULE_PARM(vra, "i");
 MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
 
+#ifdef I3_PATCH
+typedef enum {SPDIF_PCM, SPDIF_AC3} spdif_config_t;
+#endif
+
 static struct au1550_state {
 	/* soundcore stuff */
 	int             dev_audio;
@@ -119,7 +135,12 @@
 	mode_t          open_mode;
 	wait_queue_head_t open_wait;
 
+        int             output_disabled;     // for triggers
+
 	struct dmabuf {
+#ifdef I3_PATCH
+                int		allocated;   // ddma channels allocated
+#endif
 		u32		dmanr;
 		unsigned        sample_rate;
 		unsigned	src_factor;
@@ -472,6 +493,114 @@
 	adc->sample_rate = adc_rate;
 }
 
+#ifdef I3_PATCH
+// i3 patch
+static void disable_SPDIF(struct ac97_codec *codec)
+{
+  u32 val;
+
+  val = rdcodec(codec, AC97_EXTENDED_STATUS);
+  val &= ~AC97_EA_SPDIF;
+  wrcodec(codec, AC97_EXTENDED_STATUS, val);
+}
+
+// i3 patch
+static void enable_SPDIF(struct ac97_codec *codec)
+{
+  u32 val;
+
+  val = rdcodec(codec, AC97_EXTENDED_STATUS);
+  val |= AC97_EA_SPDIF;
+  wrcodec(codec, AC97_EXTENDED_STATUS, val);
+}
+
+
+// i3 patch
+static void stop_dac(struct au1550_state *s);
+static void stop_adc(struct au1550_state *s);
+static void set_xmit_slots(int num_channels);
+static void setup_spdif(struct au1550_state *s, spdif_config_t sc)
+{        
+	struct ac97_codec *codec = s->codec;
+        u16 val;
+
+        stop_dac(s);
+        stop_adc(s);
+
+        // Disable spdif before fiddling the registers
+	disable_SPDIF(s->codec);
+
+        //
+        // AC97  Extended Status register
+        //
+        val = rdcodec(codec, AC97_EXTENDED_STATUS);
+
+        // Set which ac-link timeslots that goes out on spdif
+        val &= AC97_EA_SLOT_MASK;
+	if (sc == SPDIF_AC3)
+	  val |= AC97_EA_SPSA_7_8;
+	else
+	  val |= AC97_EA_SPSA_3_4;
+
+        wrcodec(codec, AC97_EXTENDED_STATUS, val);
+
+        
+        //
+        // AC97 SPDIF Control register
+        //
+        val = rdcodec(codec, AC97_SPDIF_CONTROL);
+
+        // This is real strange, if the flag AC97_SC_NAUDIO
+        // is set as below, then the AC97 codec must be reseted
+        // to make the flag unset. The decoder from Creative that
+        // we are using seems to be doing fine without the flag so
+        // we skip it.
+
+        // 48 kHz samplerate
+        val &= AC97_SC_SPSR_MASK;
+        val |= AC97_SC_SPSR_48K;
+
+        wrcodec(codec, AC97_SPDIF_CONTROL, val);
+
+        // Done configuring - enable spdif
+	enable_SPDIF(s->codec);
+
+        // Set AC97 xmit slots. Will also sync ac97 and spdif slots.
+        if (sc == SPDIF_PCM) {
+                // Use link slot 3&4
+                set_xmit_slots(1);
+        } else {
+                // Use link slot 7&8
+                
+                u32	ac97_config, stat;                
+
+                ac97_config = au_readl(PSC_AC97CFG);
+                au_sync();
+                ac97_config &= ~(PSC_AC97CFG_TXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
+                au_writel(ac97_config, PSC_AC97CFG);
+                au_sync();
+                
+                ac97_config |= PSC_AC97CFG_TXSLOT_ENA(7);
+                ac97_config |= PSC_AC97CFG_TXSLOT_ENA(8);          
+                
+                au_writel(ac97_config, PSC_AC97CFG);
+                au_sync();
+                
+                ac97_config |= PSC_AC97CFG_DE_ENABLE;
+                au_writel(ac97_config, PSC_AC97CFG);
+                au_sync();
+                
+                /* Wait for Device ready.
+                 */
+                do {
+                        stat = readl(PSC_AC97STAT);
+                        au_sync();
+                } while ((stat & PSC_AC97STAT_DR) == 0);                
+        }
+        
+}
+#endif // I3_PATCH
+
 static void
 stop_dac(struct au1550_state *s)
 {
@@ -682,7 +811,7 @@
 	if (!db->rawbuf) {
 		db->ready = db->mapped = 0;
 		db->buforder = 5;	/* 32 * PAGE_SIZE */
-		db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);
+		db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL); // 128 kB
 		if (!db->rawbuf)
 			return -ENOMEM;
 	}
@@ -760,6 +889,89 @@
 	return prog_dmabuf(s, &s->dma_dac);
 }
 
+#ifdef I3_PATCH
+// Interrupt routine when we are running a dma from rx to tx fifo
+static void
+adc2dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct au1550_state *s = (struct au1550_state *) dev_id;
+	struct dmabuf  *db = &s->dma_adc;
+        int n;
+
+        // Make sure that we keep the descriptor ring full since
+        // it seems like we are loosing interrupts from the ddma.
+        for (n = 0; n < NUM_DBDMA_DESCRIPTORS; n++)
+                if (au1xxx_dbdma_put_dscr2(db->dmanr, DMA_RXTX_LOOP_TRANSFER_SIZE) == 0)
+                        break;
+
+}
+
+// Start a dma transfers from rx to tx fifo.
+// Use stop_adc() to stop.
+static void
+start_dma_rxtx_loop(struct au1550_state *s)
+{
+	struct dmabuf  *db = &s->dma_adc;
+	int	i;
+
+	if (!db->stopped)
+		return;
+
+
+	/* Put two buffers on the ring to get things started.
+	*/
+	for (i = 0; i < 2; i++)
+                au1xxx_dbdma_put_dscr2(db->dmanr, DMA_RXTX_LOOP_TRANSFER_SIZE);
+        
+        // Clear RX/TX fifo
+	au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
+	au_sync();
+	au_writel(PSC_AC97PCR_RS, PSC_AC97PCR);
+	au_sync();
+	au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
+	au_sync();
+	au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
+	au_sync();
+
+        // Start dma
+	au1xxx_dbdma_start(db->dmanr);
+
+	db->stopped = 0;
+}
+
+
+// Allocate dma resources for rx to tx transfer
+static void
+setup_dma_rxtx_loop(struct au1550_state *s)
+{
+        // Free current dma configuration
+        if (s->dma_adc.allocated)
+                au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+        if (s->dma_dac.allocated)
+                au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
+        s->dma_dac.allocated = s->dma_adc.allocated = 0;
+
+        // Setup adc fifo to dac fifo dma
+	if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
+	    DBDMA_AC97_TX_CHAN, adc2dac_dma_interrupt, (void *)s)) == 0) {
+		err("Can't get ADC DMA for adc2dac");
+		goto err_dma;
+	}
+        s->dma_adc.allocated = 1;
+	au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
+
+	if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
+					NUM_DBDMA_DESCRIPTORS) == 0) {
+		err("Can't get ADC DMA descriptors");
+		goto err_dma;
+	}
+
+	//info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);        
+
+err_dma:
+        return;
+}
+#endif // I3_PATCH
 
 /* hold spinlock for the following */
 static void
@@ -779,8 +991,9 @@
 	   completed.
 	*/
 	db->dma_qcount--;
+	db->count -= db->fragsize;
 
-	if (db->count >= db->fragsize) {
+	if (db->count >= (db->dma_qcount + 1) * db->fragsize) {
 		if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
 							db->fragsize) == 0) {
 			err("qcount < 2 and no ring room1!");
@@ -788,7 +1001,6 @@
 		db->nextOut += db->fragsize;
 		if (db->nextOut >= db->rawbuf + db->dmasize)
 			db->nextOut -= db->dmasize;
-		db->count -= db->fragsize;
 		db->total_bytes += db->dma_fragsize;
 		db->dma_qcount++;
 	}
@@ -1227,7 +1439,7 @@
 		 * on the dma queue.  If the queue count reaches zero,
 		 * we know the dma has stopped.
 		 */
-		while ((db->dma_qcount < 2) && (db->count >= db->fragsize)) {
+		while ((db->dma_qcount < 2) && (db->count >= (db->dma_qcount + 1) * db->fragsize)) {
 			if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
 							db->fragsize) == 0) {
 				err("qcount < 2 and no ring room!0");
@@ -1236,8 +1448,12 @@
 			if (db->nextOut >= db->rawbuf + db->dmasize)
 				db->nextOut -= db->dmasize;
 			db->total_bytes += db->dma_fragsize;
-			if (db->dma_qcount == 0)
-				start_dac(s);
+			if (db->dma_qcount == 0) {
+                                // Do not start dac if user explicitly has disabled output
+                                // with SNDCTL_DSP_SETTRIGGER
+                                if (!s->output_disabled)
+                                        start_dac(s);
+                        }
 			db->dma_qcount++;
 		}
 		spin_unlock_irqrestore(&s->lock, flags);
@@ -1436,6 +1652,7 @@
 			stop_dac(s);
 			synchronize_irq();
 			s->dma_dac.count = s->dma_dac.total_bytes = 0;
+                        s->dma_dac.dma_qcount = 0;
 			s->dma_dac.nextIn = s->dma_dac.nextOut =
 				s->dma_dac.rawbuf;
 		}
@@ -1595,6 +1812,19 @@
 				stop_dac(s);
 				if (val == AFMT_S16_LE)
 					s->dma_dac.sample_size = 16;
+
+#ifdef I3_PATCH
+                                // i3 patch  AC3
+                                else  if (val == AFMT_AC3) {
+                                        s->dma_dac.sample_size = 16;
+                                        s->dma_dac.num_channels = 2;
+                                        set_dac_rate(s, 48*1000);
+                                        stop_dac(s);
+                                        stop_adc(s);
+                                        // Setup spdif with ac97 timeslot selected for AC3.
+                                        setup_spdif(s, SPDIF_AC3); 
+                                }
+#endif
 				else {
 					val = AFMT_U8;
 					s->dma_dac.sample_size = 8;
@@ -1635,6 +1865,7 @@
 				stop_adc(s);
 		}
 		if (file->f_mode & FMODE_WRITE) {
+                        s->output_disabled = ((val & PCM_ENABLE_OUTPUT) == 0);
 			if (val & PCM_ENABLE_OUTPUT)
 				start_dac(s);
 			else
@@ -1816,6 +2047,12 @@
 			return put_user(s->dma_adc.sample_size, (int *)arg);
 		else
 			return put_user(s->dma_dac.sample_size, (int *)arg);
+#ifdef I3_PATCH
+        case SNDCTL_DSP_RXTX_LOOP:
+                setup_dma_rxtx_loop(s);
+                start_dma_rxtx_loop(s);
+                return 0;
+#endif
 
 	case SOUND_PCM_WRITE_FILTER:
 	case SNDCTL_DSP_SETSYNCRO:
@@ -1826,6 +2063,67 @@
 	return mixdev_ioctl(s->codec, cmd, arg);
 }
 
+#ifdef I3_PATCH
+// i3 patch
+static void setup_dma(struct au1550_state *s)
+{
+        // Free DMA channels if already allocated
+        if (s->dma_adc.allocated)
+                au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+        if (s->dma_dac.allocated)
+                au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
+        s->dma_adc.allocated= s->dma_dac.allocated = 0;
+
+	/* Allocate the DMA Channels
+	*/
+	if ((s->dma_dac.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_MEM_CHAN,
+	    DBDMA_AC97_TX_CHAN, dac_dma_interrupt, (void *)s)) == 0) {
+		err("Can't get DAC DMA");
+		goto err_dma1;
+	}
+	au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
+
+	// i3 patch to remove half-word swapping made by the DDMA
+	{
+	  chan_tab_t        *ctp;
+	  dbdev_tab_t       *stp;
+	  ctp = *((chan_tab_t **)(s->dma_dac.dmanr));
+	  stp = ctp->chan_src;
+	  stp->dev_devwidth = 16;
+	}
+	// end i3 patch
+
+	if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
+					NUM_DBDMA_DESCRIPTORS) == 0) {
+		err("Can't get DAC DMA descriptors");
+		goto err_dma1;
+	}
+
+	if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
+	    DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
+		err("Can't get ADC DMA");
+		goto err_dma2;
+	}
+	au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
+	if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
+					NUM_DBDMA_DESCRIPTORS) == 0) {
+		err("Can't get ADC DMA descriptors");
+		goto err_dma2;
+	}
+
+        s->dma_adc.allocated = s->dma_dac.allocated = 1;
+
+	//info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
+
+        return;
+
+err_dma1:
+	au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+ err_dma2:
+	au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
+
+}
+#endif // I3_PATCH
 
 static int
 au1550_open(struct inode *inode, struct file *file)
@@ -1861,6 +2159,12 @@
 		down(&s->open_sem);
 	}
 
+#ifdef I3_PATCH
+        // i3 patch
+        setup_spdif(s, SPDIF_PCM);
+        setup_dma(s);
+        s->output_disabled = 0;
+#endif
 	if (file->f_mode & FMODE_READ) {
 		stop_adc(s);
 		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
@@ -1985,6 +2289,19 @@
 		goto err_dma1;
 	}
 	au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
+
+#ifdef I3_PATCH
+	// i3 patch to fix bug with half-word swapping made by the DDMA (not really noticed when
+        // playing PCM but fatal when sendinf AC3 on spdif)
+	{
+	  chan_tab_t        *ctp;
+	  dbdev_tab_t       *stp;
+	  ctp = *((chan_tab_t **)(s->dma_dac.dmanr));
+	  stp = ctp->chan_src;
+	  stp->dev_devwidth = 16;
+	}
+	// end i3 patch
+#endif // I3_PATCH
 	if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
 					NUM_DBDMA_DESCRIPTORS) == 0) {
 		err("Can't get DAC DMA descriptors");
@@ -2003,7 +2320,7 @@
 		goto err_dma2;
 	}
 
-	info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
+	//info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
 
 	/* register devices */
 
@@ -2119,6 +2436,11 @@
 					     ac97_read_proc, &s->codec);
 #endif
 
+#ifdef I3_PATCH
+        s->dma_adc.allocated = s->dma_dac.allocated = 1;
+        setup_spdif(s, SPDIF_PCM);	
+#endif // I3_PATCH
+
 	set_xmit_slots(1);
 	set_recv_slots(1);
 
@@ -2151,8 +2473,21 @@
 		remove_proc_entry(AU1000_MODULE_NAME, NULL);
 #endif /* AU1000_DEBUG */
 	synchronize_irq();
+#ifdef I3_PATCH
+        if (s->dma_adc.allocated) 			// i3 patch
+        {
+#endif
 	au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+#ifdef I3_PATCH
+        }
+        if (s->dma_dac.allocated) 			// i3 patch
+        {
+#endif
 	au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
+#ifdef I3_PATCH
+        }
+        s->dma_adc.allocated = s->dma_dac.allocated = 0;// i3 patch
+#endif
 	release_region(PHYSADDR(AC97_PSC_SEL), 0x30);
 	unregister_sound_dsp(s->dev_audio);
 	unregister_sound_mixer(s->codec->dev_mixer);
diff -x asm -Naur linux-2.4.30-orig/fs/Config.in linux-2.4.30/fs/Config.in
--- linux-2.4.30-orig/fs/Config.in	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/Config.in	2006-12-11 18:18:37.000000000 +0100
@@ -49,8 +49,34 @@
 dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD
 if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then
    int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
-   bool 'JFFS2 support for NAND chips' CONFIG_JFFS2_FS_NAND
+   bool 'JFFS2 write-buffering support' CONFIG_JFFS2_FS_WRITEBUFFER
+   bool 'JFFS2 ZLIB compression support (recommended)' CONFIG_JFFS2_ZLIB
+   bool 'JFFS2 RTIME compression support (recommended)' CONFIG_JFFS2_RTIME
+   bool 'JFFS2 RUBIN compression support' CONFIG_JFFS2_RUBIN
+   bool 'JFFS2 LZO compression support' CONFIG_JFFS2_LZO
+   bool 'JFFS2 LZARI compression support' CONFIG_JFFS2_LZARI
+   choice 'JFFS2 default compression mode' \
+        "none                                   CONFIG_JFFS2_CMODE_NONE \
+         priority                               CONFIG_JFFS2_CMODE_PRIORITY \
+         size                                   CONFIG_JFFS2_CMODE_SIZE" priority
+
+   bool 'JFFS2 proc interface support' CONFIG_JFFS2_PROC
+fi
+
+# Yaffs
+tristate 'Yet Another Flash Filing System(YAFFS) file system support' CONFIG_YAFFS_FS
+if [ "$CONFIG_YAFFS_FS" = "y" -o "$CONFIG_YAFFS_FS" = "m" ] ; then
+   bool 'NAND mtd support' CONFIG_YAFFS_MTD_ENABLED y
+   bool 'yaffsram file system support' CONFIG_YAFFS_RAM_ENABLED n
+   bool 'Old mtd support' CONFIG_YAFFS_USE_OLD_MTD n
+   bool 'Use ECC functions of the generic MTD-NAND driver' CONFIG_YAFFS_USE_NANDECC y
+   bool 'Use the same ecc byte order as Steven Hills nand_ecc.c' CONFIG_YAFFS_ECC_WRONG_ORDER n
+   bool 'Use Linux file caching layer' CONFIG_YAFFS_USE_GENERIC_RW y
+   bool 'Use object header size' CONFIG_YAFFS_USE_HEADER_FILE_SIZE n
+   bool 'Turn off debug chunk erase check' CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK n
+   bool 'Cache short names in RAM' CONFIG_YAFFS_SHORT_NAMES_IN_RAM y
 fi
+
 tristate 'Compressed ROM file system support' CONFIG_CRAMFS
 bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
 define_bool CONFIG_RAMFS y
diff -x asm -Naur linux-2.4.30-orig/fs/Makefile linux-2.4.30/fs/Makefile
--- linux-2.4.30-orig/fs/Makefile	2005-05-12 17:57:35.000000000 +0200
+++ linux-2.4.30/fs/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -54,6 +54,7 @@
 subdir-$(CONFIG_EFS_FS)		+= efs
 subdir-$(CONFIG_JFFS_FS)	+= jffs
 subdir-$(CONFIG_JFFS2_FS)	+= jffs2
+subdir-$(CONFIG_YAFFS_FS)	+= yaffs
 subdir-$(CONFIG_AFFS_FS)	+= affs
 subdir-$(CONFIG_ROMFS_FS)	+= romfs
 subdir-$(CONFIG_QNX4FS_FS)	+= qnx4
diff -x asm -Naur linux-2.4.30-orig/fs/fcntl.c linux-2.4.30/fs/fcntl.c
--- linux-2.4.30-orig/fs/fcntl.c	2005-05-12 17:57:35.000000000 +0200
+++ linux-2.4.30/fs/fcntl.c	2006-12-11 18:18:37.000000000 +0100
@@ -200,7 +200,7 @@
 	return ret;
 }
 
-#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT)
+#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT | O_STREAMING)
 
 static int setfl(int fd, struct file * filp, unsigned long arg)
 {
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/Makefile linux-2.4.30/fs/jffs2/Makefile
--- linux-2.4.30-orig/fs/jffs2/Makefile	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -1,40 +1,43 @@
 #
-# Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
+# fs/jffs2/Makefile.24
 #
-# $Id: Makefile.common,v 1.1 2003/05/27 09:34:41 dwmw2 Exp $
-#
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now in the main makefile...
+# $Id: Makefile,v 1.2 2005/09/28 14:19:39 lel Exp $
 
+ifdef OUT_OF_TREE_BUILD
+include $(mtd)/defconfig
 
-obj-$(CONFIG_JFFS2_FS) := jffs2.o
+# This must be first in the include path, so it goes in $(CC) rather
+# then $(EXTRA_CFLAGS)
 
-COMPR_OBJS	:= compr.o compr_rubin.o compr_rtime.o compr_zlib.o
-JFFS2_OBJS	:= dir.o file.o ioctl.o nodelist.o malloc.o \
-	read.o nodemgmt.o readinode.o write.o scan.o gc.o \
-	symlink.o build.o erase.o background.o fs.o writev.o
+CC += -I$(mtd)/../../include
+EXTRA_CFLAGS := -g -Werror
 
-BELOW25		:= $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
+ifndef CONFIG_MTD
+EXTRA_CFLAGS += -DMTD_OUT_OF_TREE
+endif
 
-ifeq ($(BELOW25),y)
-LINUX_OBJS	:= super-v24.o crc32.o rbtree.o
-else
-LINUX_OBJS	:= super.o
+ifdef NONAND
+EXTRA_CFLAGS += -DNONAND
+endif
 endif
 
-NAND_OBJS-$(CONFIG_JFFS2_FS_NAND)	:= wbuf.o
+obj-$(CONFIG_JFFS2_FS) += jffs2.o
 
-jffs2-objs := $(COMPR_OBJS) $(JFFS2_OBJS) $(VERS_OBJS) $(NAND_OBJS-y) \
-	$(LINUX_OBJS)
+JFFS2_OBJS := compr.o dir.o file.o ioctl.o nodelist.o malloc.o
+JFFS2_OBJS += read.o nodemgmt.o readinode.o write.o scan.o gc.o
+JFFS2_OBJS += symlink-v24.o build.o erase.o background.o fs.o writev.o
 
+#LINUX_OBJS += super-v24.o crc32.o rbtree.o
+LINUX_OBJS += super-v24.o crc32.o
 
-# 2.4 build compatibility
-ifeq ($(BELOW25),y)
-obj-y := $(jffs2-objs)
+WBUF_OBJS-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
+
+COMPR_OBJS-$(CONFIG_JFFS2_RUBIN)  += compr_rubin.o
+COMPR_OBJS-$(CONFIG_JFFS2_RTIME)  += compr_rtime.o
+COMPR_OBJS-$(CONFIG_JFFS2_ZLIB)   += compr_zlib.o
+
+obj-y := $(COMPR_OBJS-y) $(JFFS2_OBJS) $(LINUX_OBJS) $(WBUF_OBJS-y)
 O_TARGET := jffs2.o
+
 include $(TOPDIR)/Rules.make
-endif
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/Makefile.common linux-2.4.30/fs/jffs2/Makefile.common
--- linux-2.4.30-orig/fs/jffs2/Makefile.common	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/jffs2/Makefile.common	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,17 @@
+#
+# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
+#
+# $Id: Makefile.common,v 1.2 2005/09/28 14:19:39 lel Exp $
+#
+
+obj-$(CONFIG_JFFS2_FS) += jffs2.o
+
+jffs2-y	:= compr.o dir.o file.o ioctl.o nodelist.o malloc.o
+jffs2-y	+= read.o nodemgmt.o readinode.o write.o scan.o gc.o
+jffs2-y	+= symlink.o build.o erase.o background.o fs.o writev.o
+jffs2-y	+= super.o
+
+jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER)	+= wbuf.o
+jffs2-$(CONFIG_JFFS2_RUBIN)	+= compr_rubin.o
+jffs2-$(CONFIG_JFFS2_RTIME)	+= compr_rtime.o
+jffs2-$(CONFIG_JFFS2_ZLIB)	+= compr_zlib.o
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/TODO linux-2.4.30/fs/jffs2/TODO
--- linux-2.4.30-orig/fs/jffs2/TODO	2005-05-12 17:57:37.000000000 +0200
+++ linux-2.4.30/fs/jffs2/TODO	2006-12-11 18:18:37.000000000 +0100
@@ -1,4 +1,4 @@
-$Id: TODO,v 1.3 2001/03/01 23:26:48 dwmw2 Exp $
+$Id: TODO,v 1.2 2005/09/28 14:19:39 lel Exp $
 
  - disable compression in commit_write()? Or at least optimise the 'always write
 	whole page' bit.
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/background.c linux-2.4.30/fs/jffs2/background.c
--- linux-2.4.30-orig/fs/jffs2/background.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/fs/jffs2/background.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,22 +3,18 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: background.c,v 1.48 2004/02/23 17:17:23 dwmw2 Exp $
+ * $Id: background.c,v 1.2 2005/09/28 14:19:39 lel Exp $
  *
  */
 
-#define __KERNEL_SYSCALLS__
-
 #include <linux/kernel.h>
 #include <linux/jffs2.h>
 #include <linux/mtd/mtd.h>
 #include <linux/completion.h>
-#include <linux/sched.h>
-#include <linux/unistd.h>
 #include <linux/suspend.h>
 #include "nodelist.h"
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/build.c linux-2.4.30/fs/jffs2/build.c
--- linux-2.4.30-orig/fs/jffs2/build.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/fs/jffs2/build.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,17 +3,19 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $
+ * $Id: build.c,v 1.2 2005/09/28 14:19:40 lel Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
 #include "nodelist.h"
 
 static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
@@ -62,6 +64,7 @@
 		if (!child_ic) {
 			printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
 				  fd->name, fd->ino, ic->ino);
+			jffs2_mark_node_obsolete(c, fd->raw);
 			continue;
 		}
 
@@ -88,21 +91,22 @@
 	int ret;
 	int i;
 	struct jffs2_inode_cache *ic;
+	struct jffs2_full_dirent *fd;
 	struct jffs2_full_dirent *dead_fds = NULL;
 
 	/* First, scan the medium and build all the inode caches with
 	   lists of physical nodes */
 
-	c->flags |= JFFS2_SB_FLAG_MOUNTING;
+	c->flags |= JFFS2_SB_FLAG_SCANNING;
 	ret = jffs2_scan_medium(c);
-	c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
-
+	c->flags &= ~JFFS2_SB_FLAG_SCANNING;
 	if (ret)
-		return ret;
+		goto exit;
 
 	D1(printk(KERN_DEBUG "Scanned flash completely\n"));
-	D1(jffs2_dump_block_lists(c));
+	D2(jffs2_dump_block_lists(c));
 
+	c->flags |= JFFS2_SB_FLAG_BUILDING;
 	/* Now scan the directory tree, increasing nlink according to every dirent found. */
 	for_each_inode(i, c, ic) {
 		D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
@@ -114,6 +118,7 @@
 			cond_resched();
 		}
 	}
+
 	D1(printk(KERN_DEBUG "Pass 1 complete\n"));
 
 	/* Next, scan for inodes with nlink == 0 and remove them. If
@@ -135,9 +140,7 @@
 	D1(printk(KERN_DEBUG "Pass 2a starting\n"));
 
 	while (dead_fds) {
-		struct jffs2_inode_cache *ic;
-		struct jffs2_full_dirent *fd = dead_fds;
-
+		fd = dead_fds;
 		dead_fds = fd->next;
 
 		ic = jffs2_get_ino_cache(c, fd->ino);
@@ -152,7 +155,6 @@
 	
 	/* Finally, we can scan again and free the dirent structs */
 	for_each_inode(i, c, ic) {
-		struct jffs2_full_dirent *fd;
 		D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
 
 		while(ic->scan_dents) {
@@ -163,12 +165,27 @@
 		ic->scan_dents = NULL;
 		cond_resched();
 	}
+	c->flags &= ~JFFS2_SB_FLAG_BUILDING;
+	
 	D1(printk(KERN_DEBUG "Pass 3 complete\n"));
-	D1(jffs2_dump_block_lists(c));
+	D2(jffs2_dump_block_lists(c));
 
 	/* Rotate the lists by some number to ensure wear levelling */
 	jffs2_rotate_lists(c);
 
+	ret = 0;
+
+exit:
+	if (ret) {
+		for_each_inode(i, c, ic) {
+			while(ic->scan_dents) {
+				fd = ic->scan_dents;
+				ic->scan_dents = fd->next;
+				jffs2_free_full_dirent(fd);
+			}
+		}
+	}
+
 	return ret;
 }
 
@@ -179,9 +196,12 @@
 
 	D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
 	
-	for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
+	raw = ic->nodes;
+	while (raw != (void *)ic) {
+		struct jffs2_raw_node_ref *next = raw->next_in_ino;
 		D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
 		jffs2_mark_node_obsolete(c, raw);
+		raw = next;
 	}
 
 	if (ic->scan_dents) {
@@ -297,7 +317,10 @@
 
 	c->free_size = c->flash_size;
 	c->nr_blocks = c->flash_size / c->sector_size;
-	c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
+ 	if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
+		c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks);
+	else
+		c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
 	if (!c->blocks)
 		return -ENOMEM;
 	for (i=0; i<c->nr_blocks; i++) {
@@ -310,6 +333,7 @@
 		c->blocks[i].used_size = 0;
 		c->blocks[i].first_node = NULL;
 		c->blocks[i].last_node = NULL;
+		c->blocks[i].bad_count = 0;
 	}
 
 	init_MUTEX(&c->alloc_sem);
@@ -336,7 +360,11 @@
 		D1(printk(KERN_DEBUG "build_fs failed\n"));
 		jffs2_free_ino_caches(c);
 		jffs2_free_raw_node_refs(c);
-		kfree(c->blocks);
+		if (c->mtd->flags & MTD_NO_VIRTBLOCKS) {
+			vfree(c->blocks);
+		} else {
+			kfree(c->blocks);
+		}
 		return -EIO;
 	}
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/compr.c linux-2.4.30/fs/jffs2/compr.c
--- linux-2.4.30-orig/fs/jffs2/compr.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/fs/jffs2/compr.c	2006-12-11 18:18:37.000000000 +0100
@@ -2,32 +2,39 @@
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
- *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                    University of Szeged, Hungary
+ *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr.c,v 1.34 2004/03/08 15:29:09 dwmw2 Exp $
+ * $Id: compr.c,v 1.2 2005/09/28 14:19:40 lel Exp $
  *
  */
 
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/jffs2.h>
-#include "nodelist.h"
-
-int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
+#include "compr.h"
+
+static DEFINE_SPINLOCK(jffs2_compressor_list_lock);
+
+/* Available compressors are on this list */
+static LIST_HEAD(jffs2_compressor_list);
 
+/* Actual compression mode */
+static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+
+void jffs2_set_compression_mode(int mode) 
+{
+        jffs2_compression_mode = mode;
+}
+
+int jffs2_get_compression_mode(void)
+{
+        return jffs2_compression_mode;
+}
+
+/* Statistics for blocks stored without compression */
+static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
 
 /* jffs2_compress:
  * @data: Pointer to uncompressed data
@@ -38,103 +45,435 @@
  *	data. On exit, expected to hold the actual size of the compressed
  *	data.
  *
- * Returns: Byte to be stored with data indicating compression type used.
+ * Returns: Lower byte to be stored with data indicating compression type used.
  * Zero is used to show that the data could not be compressed - the 
  * compressed version was actually larger than the original.
+ * Upper byte will be used later. (soon)
  *
  * If the cdata buffer isn't large enough to hold all the uncompressed data,
  * jffs2_compress should compress as much as will fit, and should set 
  * *datalen accordingly to show the amount of data which were compressed.
  */
-unsigned char jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 			     unsigned char *data_in, unsigned char **cpage_out, 
 			     uint32_t *datalen, uint32_t *cdatalen)
 {
-#ifdef JFFS2_COMPRESSION
-	int ret;
+	int ret = JFFS2_COMPR_NONE;
+        int compr_ret;
+        struct jffs2_compressor *this, *best=NULL;
+        unsigned char *output_buf = NULL, *tmp_buf;
+        uint32_t orig_slen, orig_dlen;
+        uint32_t best_slen=0, best_dlen=0;
 
-	*cpage_out = kmalloc(*cdatalen, GFP_KERNEL);
-	if (!*cpage_out) {
-		printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n");
-		goto out;
-	}
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                break;
+        case JFFS2_COMPR_MODE_PRIORITY:
+                output_buf = kmalloc(*cdatalen,GFP_KERNEL);
+                if (!output_buf) {
+                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
+                        goto out;
+                }
+                orig_slen = *datalen;
+                orig_dlen = *cdatalen;
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        /* Skip decompress-only backwards-compatibility and disabled modules */
+                        if ((!this->compress)||(this->disabled))
+                                continue;
 
-#ifdef JFFS2_USE_ZLIB
-	ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_ZLIB;
-	}
-#endif
-#ifdef JFFS2_USE_DYNRUBIN
-	ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_DYNRUBIN;
-	}
-#endif
-#ifdef JFFS2_USE_RUBINMIPS
-	ret = jffs2_rubinmips_compress(data_in, *cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_RUBINMIPS;
-	}
-#endif
-#ifdef JFFS2_USE_RTIME
-	/* rtime does manage to recompress already-compressed data */
-	ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_RTIME;
-	}
-#endif
-	kfree(*cpage_out);
-#endif /* Compression */
+                        this->usecount++;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        *datalen  = orig_slen;
+                        *cdatalen = orig_dlen;
+                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+                        spin_lock(&jffs2_compressor_list_lock);
+                        this->usecount--;
+                        if (!compr_ret) {
+                                ret = this->compr;
+                                this->stat_compr_blocks++;
+                                this->stat_compr_orig_size += *datalen;
+                                this->stat_compr_new_size  += *cdatalen;
+                                break;
+                        }
+                }
+                spin_unlock(&jffs2_compressor_list_lock);
+                if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
+                break;
+        case JFFS2_COMPR_MODE_SIZE:
+                orig_slen = *datalen;
+                orig_dlen = *cdatalen;
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        /* Skip decompress-only backwards-compatibility and disabled modules */
+                        if ((!this->compress)||(this->disabled))
+                                continue;
+                        /* Allocating memory for output buffer if necessary */
+                        if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                kfree(this->compr_buf);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                this->compr_buf_size=0;
+                                this->compr_buf=NULL;
+                        }
+                        if (!this->compr_buf) {
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                if (!tmp_buf) {
+                                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
+                                        continue;
+                                }
+                                else {
+                                        this->compr_buf = tmp_buf;
+                                        this->compr_buf_size = orig_dlen;
+                                }
+                        }
+                        this->usecount++;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        *datalen  = orig_slen;
+                        *cdatalen = orig_dlen;
+                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+                        spin_lock(&jffs2_compressor_list_lock);
+                        this->usecount--;
+                        if (!compr_ret) {
+                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
+                                        best_dlen = *cdatalen;
+                                        best_slen = *datalen;
+                                        best = this;
+                                }
+                        }
+                }
+                if (best_dlen) {
+                        *cdatalen = best_dlen;
+                        *datalen  = best_slen;
+                        output_buf = best->compr_buf;
+                        best->compr_buf = NULL;
+                        best->compr_buf_size = 0;
+                        best->stat_compr_blocks++;
+                        best->stat_compr_orig_size += best_slen;
+                        best->stat_compr_new_size  += best_dlen;
+                        ret = best->compr;
+                }
+                spin_unlock(&jffs2_compressor_list_lock);
+                break;
+        default:
+                printk(KERN_ERR "JFFS2: unknow compression mode.\n");
+        }
  out:
-	*cpage_out = data_in;
-	*datalen = *cdatalen;
-	return JFFS2_COMPR_NONE; /* We failed to compress */
-}
-
-void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
-{
-	if (orig != comprbuf)
-		kfree(comprbuf);
+        if (ret == JFFS2_COMPR_NONE) {
+	        *cpage_out = data_in;
+	        *datalen = *cdatalen;
+                none_stat_compr_blocks++;
+                none_stat_compr_size += *datalen;
+        }
+        else {
+                *cpage_out = output_buf;
+        }
+	return ret;
 }
 
 int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-		     unsigned char comprtype, unsigned char *cdata_in, 
+		     uint16_t comprtype, unsigned char *cdata_in, 
 		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
 {
-	switch (comprtype) {
+        struct jffs2_compressor *this;
+        int ret;
+
+	/* Older code had a bug where it would write non-zero 'usercompr'
+	   fields. Deal with it. */
+	if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB)
+		comprtype &= 0xff;
+
+	switch (comprtype & 0xff) {
 	case JFFS2_COMPR_NONE:
 		/* This should be special-cased elsewhere, but we might as well deal with it */
 		memcpy(data_out, cdata_in, datalen);
+                none_stat_decompr_blocks++;
 		break;
-
 	case JFFS2_COMPR_ZERO:
 		memset(data_out, 0, datalen);
 		break;
-#ifdef JFFS2_USE_ZLIB
-	case JFFS2_COMPR_ZLIB:
-		jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+	default:
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        if (comprtype == this->compr) {
+                                this->usecount++;
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                if (ret) {
+                                        printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
+                                }
+                                else {
+                                        this->stat_decompr_blocks++;
+                                }
+                                this->usecount--;
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                return ret;
+                        }
+                }
+		printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
+                spin_unlock(&jffs2_compressor_list_lock);
+		return -EIO;
+	}
+	return 0;
+}
+
+int jffs2_register_compressor(struct jffs2_compressor *comp)
+{
+        struct jffs2_compressor *this;
+
+        if (!comp->name) {
+                printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
+                return -1;
+        }
+        comp->compr_buf_size=0;
+        comp->compr_buf=NULL;
+        comp->usecount=0;
+        comp->stat_compr_orig_size=0;
+        comp->stat_compr_new_size=0;
+        comp->stat_compr_blocks=0;
+        comp->stat_decompr_blocks=0;
+        D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
+
+        spin_lock(&jffs2_compressor_list_lock);
+
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (this->priority < comp->priority) {
+                        list_add(&comp->list, this->list.prev);
+                        goto out;
+                }
+        }
+        list_add_tail(&comp->list, &jffs2_compressor_list);
+out:
+        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+        })
+
+        spin_unlock(&jffs2_compressor_list_lock);
+
+        return 0;
+}
+
+int jffs2_unregister_compressor(struct jffs2_compressor *comp)
+{
+        D2(struct jffs2_compressor *this;)
+
+        D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
+
+        spin_lock(&jffs2_compressor_list_lock);
+
+        if (comp->usecount) {
+                spin_unlock(&jffs2_compressor_list_lock);
+                printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
+                return -1;
+        }
+        list_del(&comp->list);
+
+        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+        })
+        spin_unlock(&jffs2_compressor_list_lock);
+        return 0;
+}
+
+#ifdef CONFIG_JFFS2_PROC
+
+#define JFFS2_STAT_BUF_SIZE 16000
+
+char *jffs2_list_compressors(void)
+{
+        struct jffs2_compressor *this;
+        char *buf, *act_buf;
+
+        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
+                if ((this->disabled)||(!this->compress))
+                        act_buf += sprintf(act_buf,"disabled");
+                else
+                        act_buf += sprintf(act_buf,"enabled");
+                act_buf += sprintf(act_buf,"\n");
+        }
+        return buf;
+}
+
+char *jffs2_stats(void)
+{
+        struct jffs2_compressor *this;
+        char *buf, *act_buf;
+
+        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
+
+        act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
+        act_buf += sprintf(act_buf,"%10s   ","none");
+        act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks, 
+                           none_stat_compr_size, none_stat_decompr_blocks);
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                act_buf += sprintf(act_buf,"%10s ",this->name);
+                if ((this->disabled)||(!this->compress))
+                        act_buf += sprintf(act_buf,"- ");
+                else
+                        act_buf += sprintf(act_buf,"+ ");
+                act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d)  decompr: %d blocks ", this->stat_compr_blocks, 
+                                   this->stat_compr_new_size, this->stat_compr_orig_size, 
+                                   this->stat_decompr_blocks);
+                act_buf += sprintf(act_buf,"\n");
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+
+        return buf;
+}
+
+char *jffs2_get_compression_mode_name(void) 
+{
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                return "none";
+        case JFFS2_COMPR_MODE_PRIORITY:
+                return "priority";
+        case JFFS2_COMPR_MODE_SIZE:
+                return "size";
+        }
+        return "unkown";
+}
+
+int jffs2_set_compression_mode_name(const char *name) 
+{
+        if (!strcmp("none",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+                return 0;
+        }
+        if (!strcmp("priority",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+                return 0;
+        }
+        if (!strcmp("size",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+                return 0;
+        }
+        return 1;
+}
+
+static int jffs2_compressor_Xable(const char *name, int disabled)
+{
+        struct jffs2_compressor *this;
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->disabled = disabled;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        return 0;                        
+                }
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
+        return 1;
+}
+
+int jffs2_enable_compressor_name(const char *name)
+{
+        return jffs2_compressor_Xable(name, 0);
+}
+
+int jffs2_disable_compressor_name(const char *name)
+{
+        return jffs2_compressor_Xable(name, 1);
+}
+
+int jffs2_set_compressor_priority(const char *name, int priority)
+{
+        struct jffs2_compressor *this,*comp;
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->priority = priority;
+                        comp = this;
+                        goto reinsert;
+                }
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);        
+        return 1;
+reinsert:
+        /* list is sorted in the order of priority, so if
+           we change it we have to reinsert it into the
+           good place */
+        list_del(&comp->list);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (this->priority < comp->priority) {
+                        list_add(&comp->list, this->list.prev);
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        return 0;
+                }
+        }
+        list_add_tail(&comp->list, &jffs2_compressor_list);
+        spin_unlock(&jffs2_compressor_list_lock);
+        return 0;
+}
+
 #endif
-#ifdef JFFS2_USE_RTIME
-	case JFFS2_COMPR_RTIME:
-		jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
+{
+        if (orig != comprbuf)
+                kfree(comprbuf);
+}
+
+int jffs2_compressors_init(void) 
+{
+/* Registering compressors */
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_init();
 #endif
-#ifdef JFFS2_USE_RUBINMIPS
-	case JFFS2_COMPR_RUBINMIPS:
-		jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_init();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_rubinmips_init();
+        jffs2_dynrubin_init();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_init();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_init();
+#endif
+/* Setting default compression mode */
+#ifdef CONFIG_JFFS2_CMODE_NONE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+        D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
+#else
+#ifdef CONFIG_JFFS2_CMODE_SIZE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+        D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
+#else
+        D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
 #endif
-#ifdef JFFS2_USE_DYNRUBIN
-	case JFFS2_COMPR_DYNRUBIN:
+#endif
+        return 0;
+}
 
-		jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+int jffs2_compressors_exit(void) 
+{
+/* Unregistering compressors */
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_exit();
 #endif
-	default:
-		printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
-		return -EIO;
-	}
-	return 0;
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_exit();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_dynrubin_exit();
+        jffs2_rubinmips_exit();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_exit();
+#endif
+        return 0;
 }
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/compr.h linux-2.4.30/fs/jffs2/compr.h
--- linux-2.4.30-orig/fs/jffs2/compr.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/jffs2/compr.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,118 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                    University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in the 
+ * jffs2 directory.
+ *
+ * $Id: compr.h,v 1.2 2005/09/28 14:19:40 lel Exp $
+ *
+ */
+
+#ifndef __JFFS2_COMPR_H__
+#define __JFFS2_COMPR_H__
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/jffs2.h>
+#include <linux/jffs2_fs_i.h>
+#include <linux/jffs2_fs_sb.h>
+#include "nodelist.h"
+
+#define JFFS2_RUBINMIPS_PRIORITY 10
+#define JFFS2_DYNRUBIN_PRIORITY  20
+#define JFFS2_LZARI_PRIORITY     30
+#define JFFS2_LZO_PRIORITY       40
+#define JFFS2_RTIME_PRIORITY     50
+#define JFFS2_ZLIB_PRIORITY      60
+
+#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
+#define JFFS2_DYNRUBIN_DISABLED  /*        for decompression */
+
+#define JFFS2_COMPR_MODE_NONE       0
+#define JFFS2_COMPR_MODE_PRIORITY   1
+#define JFFS2_COMPR_MODE_SIZE       2
+
+void jffs2_set_compression_mode(int mode);
+int jffs2_get_compression_mode(void);
+
+struct jffs2_compressor {
+        struct list_head list;
+        int priority;              /* used by prirority comr. mode */
+        char *name;
+        char compr;                /* JFFS2_COMPR_XXX */
+        int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
+                        uint32_t *srclen, uint32_t *destlen, void *model);
+        int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
+                        uint32_t cdatalen, uint32_t datalen, void *model);
+        int usecount;
+        int disabled;              /* if seted the compressor won't compress */
+        unsigned char *compr_buf;  /* used by size compr. mode */
+        uint32_t compr_buf_size;   /* used by size compr. mode */
+        uint32_t stat_compr_orig_size;
+        uint32_t stat_compr_new_size;
+        uint32_t stat_compr_blocks;
+        uint32_t stat_decompr_blocks;
+};
+
+int jffs2_register_compressor(struct jffs2_compressor *comp);
+int jffs2_unregister_compressor(struct jffs2_compressor *comp);
+
+int jffs2_compressors_init(void);
+int jffs2_compressors_exit(void);
+
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                             unsigned char *data_in, unsigned char **cpage_out,
+                             uint32_t *datalen, uint32_t *cdatalen);
+
+int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                     uint16_t comprtype, unsigned char *cdata_in,
+                     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
+
+#ifdef CONFIG_JFFS2_PROC
+int jffs2_enable_compressor_name(const char *name);
+int jffs2_disable_compressor_name(const char *name);
+int jffs2_set_compression_mode_name(const char *mode_name);
+char *jffs2_get_compression_mode_name(void);
+int jffs2_set_compressor_priority(const char *mode_name, int priority);
+char *jffs2_list_compressors(void);
+char *jffs2_stats(void);
+#endif
+
+/* Compressor modules */
+/* These functions will be called by jffs2_compressors_init/exit */
+
+#ifdef CONFIG_JFFS2_RUBIN
+int jffs2_rubinmips_init(void);
+void jffs2_rubinmips_exit(void);
+int jffs2_dynrubin_init(void);
+void jffs2_dynrubin_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+int jffs2_rtime_init(void);
+void jffs2_rtime_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+int jffs2_lzari_init(void);
+void jffs2_lzari_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_LZO
+int jffs2_lzo_init(void);
+void jffs2_lzo_exit(void);
+#endif
+
+#endif /* __JFFS2_COMPR_H__ */
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/compr_lzari.c linux-2.4.30/fs/jffs2/compr_lzari.c
--- linux-2.4.30-orig/fs/jffs2/compr_lzari.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/jffs2/compr_lzari.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,717 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Patrik Kluba,
+ *                    University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id: compr_lzari.c,v 1.2 2005/09/28 14:19:40 lel Exp $
+ *
+ */
+
+/*
+   Lempel-Ziv-Arithmetic coding compression module for jffs2
+   Based on the LZARI source included in LDS (lossless datacompression sources)
+*/
+
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+Original copyright follows:
+
+**************************************************************
+	LZARI.C -- A Data Compression Program
+	(tab = 4 spaces)
+**************************************************************
+	4/7/1989 Haruhiko Okumura
+	Use, distribute, and modify this program freely.
+	Please send me your improved versions.
+		PC-VAN		SCIENCE
+		NIFTY-Serve	PAF01022
+		CompuServe	74050,1022
+**************************************************************
+
+LZARI.C (c)1989 by Haruyasu Yoshizaki, Haruhiko Okumura, and Kenji Rikitake.
+All rights reserved. Permission granted for non-commercial use.
+
+*/
+
+/*
+
+	2004-02-18  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
+				Removed unused variables and fixed no return value
+
+	2004-02-16  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
+				Initial release
+
+*/
+
+/* lzari.c */
+
+#define N		 4096	/* size of ring buffer */
+#define F		   60	/* upper limit for match_length */
+#define THRESHOLD	2   /* encode string into position and length
+						   if match_length is greater than this */
+#define NIL			N	/* index for root of binary search trees */
+
+static unsigned char
+		text_buf[N + F - 1];	/* ring buffer of size N,
+			with extra F-1 bytes to facilitate string comparison */
+static unsigned long		match_position, match_length,  /* of longest match.  These are
+			set by the InsertNode() procedure. */
+		lson[N + 1], rson[N + 257], dad[N + 1];  /* left & right children &
+			parents -- These constitute binary search trees. */
+
+static void InitTree(void)  /* Initialize trees */
+{
+	unsigned long  i;
+
+	/* For i = 0 to N - 1, rson[i] and lson[i] will be the right and
+	   left children of node i.  These nodes need not be initialized.
+	   Also, dad[i] is the parent of node i.  These are initialized to
+	   NIL (= N), which stands for 'not used.'
+	   For i = 0 to 255, rson[N + i + 1] is the root of the tree
+	   for strings that begin with character i.  These are initialized
+	   to NIL.  Note there are 256 trees. */
+
+	for (i = N + 1; i <= N + 256; i++) rson[i] = NIL;	/* root */
+	for (i = 0; i < N; i++) dad[i] = NIL;	/* node */
+}
+
+static void InsertNode(unsigned long r)
+	/* Inserts string of length F, text_buf[r..r+F-1], into one of the
+	   trees (text_buf[r]'th tree) and returns the longest-match position
+	   and length via the global variables match_position and match_length.
+	   If match_length = F, then removes the old node in favor of the new
+	   one, because the old one will be deleted sooner.
+	   Note r plays double role, as tree node and position in buffer. */
+{
+	unsigned long i, p, temp;
+	unsigned char *key;
+	signed long cmp;
+
+	cmp = 1;  key = &text_buf[r];  p = N + 1 + key[0];
+	rson[r] = lson[r] = NIL;  match_length = 0;
+	for ( ; ; ) {
+		if (cmp >= 0) {
+			if (rson[p] != NIL) p = rson[p];
+			else {  rson[p] = r;  dad[r] = p;  return;  }
+		} else {
+			if (lson[p] != NIL) p = lson[p];
+			else {  lson[p] = r;  dad[r] = p;  return;  }
+		}
+		for (i = 1; i < F; i++)
+			if ((cmp = key[i] - text_buf[p + i]) != 0)  break;
+		if (i > THRESHOLD) {
+			if (i > match_length) {
+				match_position = (r - p) & (N - 1);
+				if ((match_length = i) >= F) break;
+			} else if (i == match_length) {
+				if ((temp = (r - p) & (N - 1)) < match_position)
+					match_position = temp;
+			}
+		}
+	}
+	dad[r] = dad[p];  lson[r] = lson[p];  rson[r] = rson[p];
+	dad[lson[p]] = r;  dad[rson[p]] = r;
+	if (rson[dad[p]] == p) rson[dad[p]] = r;
+	else                   lson[dad[p]] = r;
+	dad[p] = NIL;  /* remove p */
+}
+
+static void DeleteNode(unsigned long p)  /* Delete node p from tree */
+{
+	unsigned long  q;
+	
+	if (dad[p] == NIL) return;  /* not in tree */
+	if (rson[p] == NIL) q = lson[p];
+	else if (lson[p] == NIL) q = rson[p];
+	else {
+		q = lson[p];
+		if (rson[q] != NIL) {
+			do {  q = rson[q];  } while (rson[q] != NIL);
+			rson[dad[q]] = lson[q];  dad[lson[q]] = dad[q];
+			lson[q] = lson[p];  dad[lson[p]] = q;
+		}
+		rson[q] = rson[p];  dad[rson[p]] = q;
+	}
+	dad[q] = dad[p];
+	if (rson[dad[p]] == p) rson[dad[p]] = q;
+	else                   lson[dad[p]] = q;
+	dad[p] = NIL;
+}
+
+/********** Arithmetic Compression **********/
+
+/*  If you are not familiar with arithmetic compression, you should read
+		I. E. Witten, R. M. Neal, and J. G. Cleary,
+			Communications of the ACM, Vol. 30, pp. 520-540 (1987),
+	from which much have been borrowed.  */
+
+#define M   15
+
+/*	Q1 (= 2 to the M) must be sufficiently large, but not so
+	large as the unsigned long 4 * Q1 * (Q1 - 1) overflows.  */
+
+#define Q1  (1UL << M)
+#define Q2  (2 * Q1)
+#define Q3  (3 * Q1)
+#define Q4  (4 * Q1)
+#define MAX_CUM (Q1 - 1)
+
+#define N_CHAR  (256 - THRESHOLD + F)
+	/* character code = 0, 1, ..., N_CHAR - 1 */
+
+static unsigned long char_to_sym[N_CHAR], sym_to_char[N_CHAR + 1];
+static unsigned long
+	sym_freq[N_CHAR + 1],  /* frequency for symbols */
+	sym_cum[N_CHAR + 1],   /* cumulative freq for symbols */
+	position_cum[N + 1];   /* cumulative freq for positions */
+
+static void StartModel(void)  /* Initialize model */
+{
+	unsigned long ch, sym, i;
+	
+	sym_cum[N_CHAR] = 0;
+	for (sym = N_CHAR; sym >= 1; sym--) {
+		ch = sym - 1;
+		char_to_sym[ch] = sym;  sym_to_char[sym] = ch;
+		sym_freq[sym] = 1;
+		sym_cum[sym - 1] = sym_cum[sym] + sym_freq[sym];
+	}
+	sym_freq[0] = 0;  /* sentinel (!= sym_freq[1]) */
+	position_cum[N] = 0;
+	for (i = N; i >= 1; i--)
+		position_cum[i - 1] = position_cum[i] + 10000 / (i + 200);
+			/* empirical distribution function (quite tentative) */
+			/* Please devise a better mechanism! */
+}
+
+static void UpdateModel(unsigned long sym)
+{
+	unsigned long c, ch_i, ch_sym;
+	unsigned long i;
+	if (sym_cum[0] >= MAX_CUM) {
+		c = 0;
+		for (i = N_CHAR; i > 0; i--) {
+			sym_cum[i] = c;
+			c += (sym_freq[i] = (sym_freq[i] + 1) >> 1);
+		}
+		sym_cum[0] = c;
+	}
+	for (i = sym; sym_freq[i] == sym_freq[i - 1]; i--) ;
+	if (i < sym) {
+		ch_i = sym_to_char[i];    ch_sym = sym_to_char[sym];
+		sym_to_char[i] = ch_sym;  sym_to_char[sym] = ch_i;
+		char_to_sym[ch_i] = sym;  char_to_sym[ch_sym] = i;
+	}
+	sym_freq[i]++;
+	while (--i > 0) sym_cum[i]++;
+	sym_cum[0]++;
+}
+
+static unsigned long BinarySearchSym(unsigned long x)
+	/* 1      if x >= sym_cum[1],
+	   N_CHAR if sym_cum[N_CHAR] > x,
+	   i such that sym_cum[i - 1] > x >= sym_cum[i] otherwise */
+{
+	unsigned long i, j, k;
+	
+	i = 1;  j = N_CHAR;
+	while (i < j) {
+		k = (i + j) / 2;
+		if (sym_cum[k] > x) i = k + 1;  else j = k;
+	}
+	return i;
+}
+
+unsigned long BinarySearchPos(unsigned long x)
+	/* 0 if x >= position_cum[1],
+	   N - 1 if position_cum[N] > x,
+	   i such that position_cum[i] > x >= position_cum[i + 1] otherwise */
+{
+	unsigned long i, j, k;
+	
+	i = 1;  j = N;
+	while (i < j) {
+		k = (i + j) / 2;
+		if (position_cum[k] > x) i = k + 1;  else j = k;
+	}
+	return i - 1;
+}
+
+/* modified for block compression */
+/* on return, srclen will contain the number of successfully compressed bytes
+   and dstlen will contain completed compressed bytes */
+
+static int Encode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long *srclen,
+			unsigned long *dstlen)
+{
+	unsigned long c, i, len, r, s, last_match_length, sym, range;
+	unsigned long low = 0;
+	unsigned long high = Q4;
+	unsigned long shifts = 0;  /* counts for magnifying low and high around Q2 */
+	unsigned char *ip, *op;
+	unsigned long written = 0;
+	unsigned long read = 0;
+	unsigned char buffer = 0;
+	unsigned char mask = 128;
+	unsigned char *srcend = srcbuf + *srclen;
+	unsigned char *dstend = dstbuf + *dstlen;
+	ip = srcbuf;
+	op = dstbuf;
+	StartModel();
+	InitTree();  /* initialize trees */
+	s = 0;  r = N - F;
+	for (i = s; i < r; i++) text_buf[i] = ' ';  /* Clear the buffer with
+		any character that will appear often. */
+	for (len = 0; (len < F) && (ip < srcend); len++)
+		text_buf[r + len] = *(ip++);  /* Read F bytes into the last F bytes of
+			the buffer */
+	read = len;
+	for (i = 1; i <= F; i++) InsertNode(r - i);  /* Insert the F strings,
+		each of which begins with one or more 'space' characters.  Note
+		the order in which these strings are inserted.  This way,
+		degenerate trees will be less likely to occur. */
+	InsertNode(r);  /* Finally, insert the whole string just read.  The
+		global variables match_length and match_position are set. */
+	do {
+		if (match_length > len) match_length = len;  /* match_length
+			may be spuriously long near the end of text. */
+		if (match_length <= THRESHOLD) {
+			match_length = 1;  /* Not long enough match.  Send one byte. */
+			sym = char_to_sym[text_buf[r]];
+			range = high - low;
+			high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
+			low +=       (range * sym_cum[sym    ]) / sym_cum[0];
+			for ( ; ; ) {
+				if (high <= Q2) {
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						buffer |= mask;
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+				} else if (low >= Q2) {
+					buffer |= mask;
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+					low -= Q2;
+					high -= Q2;
+				} else if (low >= Q1 && high <= Q3) {
+					shifts++;
+					low -= Q1;
+					high -= Q1;
+				} else break;
+				low += low;  high += high;
+			}
+			UpdateModel(sym);
+		} else {
+			sym = char_to_sym[255 - THRESHOLD + match_length];
+			range = high - low;
+			high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
+			low +=       (range * sym_cum[sym    ]) / sym_cum[0];
+			for ( ; ; ) {
+				if (high <= Q2) {
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						buffer |= mask;
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+				} else if (low >= Q2) {
+					buffer |= mask;
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+					low -= Q2;
+					high -= Q2;
+				} else if (low >= Q1 && high <= Q3) {
+					shifts++;
+					low -= Q1;
+					high -= Q1;
+				} else break;
+				low += low;  high += high;
+			}
+			UpdateModel(sym);
+			range = high - low;
+			high = low + (range * position_cum[match_position - 1]) / position_cum[0];
+			low +=       (range * position_cum[match_position    ]) / position_cum[0];
+			for ( ; ; ) {
+				if (high <= Q2) {
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						buffer |= mask;
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+				} else {
+					if (low >= Q2) {
+						buffer |= mask;
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+						for ( ; shifts > 0; shifts--) {
+							if ((mask >>= 1) == 0) {
+								if (op >= dstend) {
+									*dstlen = written;
+									return -1;
+								}
+								*(op++) = buffer;
+								buffer = 0;
+								mask = 128;
+								written++;
+								*srclen = read;
+							}
+						}
+						low -= Q2;
+						high -= Q2;
+					} else {
+						if ((low >= Q1) && (high <= Q3)) {
+							shifts++;
+							low -= Q1;
+							high -= Q1;
+						} else {
+							break;
+						}
+					}
+				}
+				low += low;
+				high += high;
+			}
+		}
+		last_match_length = match_length;
+		for (i = 0; (i < last_match_length) && (ip < srcend); i++) {
+			c = *(ip++);
+			DeleteNode(s);
+			text_buf[s] = c;
+			if (s < F - 1)
+				text_buf[s + N] = c;
+			s = (s + 1) & (N - 1);
+			r = (r + 1) & (N - 1);
+			InsertNode(r);
+		}
+		read += i;
+		while (i++ < last_match_length) {
+			DeleteNode(s);
+			s = (s + 1) & (N - 1);
+			r = (r + 1) & (N - 1);
+			if (--len) InsertNode(r);
+		}
+	} while (len > 0);
+	shifts++;
+	if (low < Q1) {
+		if ((mask >>= 1) == 0) {
+			if (op >= dstend) {
+				*dstlen = written;
+				return -1;
+			}
+			*(op++) = buffer;
+			buffer = 0;
+			mask = 128;
+			written++;
+			*srclen = read;
+		}
+		for ( ; shifts > 0; shifts--) {
+			buffer |= mask;
+			if ((mask >>= 1) == 0) {
+				if (op >= dstend) {
+					*dstlen = written;
+					return -1;
+				}
+				*(op++) = buffer;
+				buffer = 0;
+				mask = 128;
+				written++;
+				*srclen = read;
+			}
+		}
+	} else {
+		buffer |= mask;
+		if ((mask >>= 1) == 0) {
+			if (op >= dstend) {
+				*dstlen = written;
+				return -1;
+			}
+			*(op++) = buffer;
+			buffer = 0;
+			mask = 128;
+			written++;
+			*srclen = read;
+		}
+		for ( ; shifts > 0; shifts--) {
+			if ((mask >>= 1) == 0) {
+				if (op >= dstend) {
+					*dstlen = written;
+					return -1;
+				}
+				*(op++) = buffer;
+				buffer = 0;
+				mask = 128;
+				written++;
+				*srclen = read;
+			}
+		}
+	}
+	for (i = 0; i < 7; i++) {
+		if ((mask >>= 1) == 0) {
+			if (op >= dstend) {
+				*dstlen = written;
+				return -1;
+			}
+			*(op++) = buffer;
+			buffer = 0;
+			mask = 128;
+			written++;
+			*srclen = read;
+		}
+	}
+	*dstlen = written;
+	return 0;
+}
+
+static int Decode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long srclen,
+					unsigned long dstlen)	/* Just the reverse of Encode(). */
+{
+	unsigned long i, r, j, k, c, range, sym;
+	unsigned char *ip, *op;
+	unsigned char *srcend = srcbuf + srclen;
+	unsigned char *dstend = dstbuf + dstlen;
+	unsigned char buffer = 0;
+	unsigned char mask = 0;
+	unsigned long low = 0;
+	unsigned long high = Q4;
+	unsigned long value = 0;
+	ip = srcbuf;
+	op = dstbuf;
+	for (i = 0; i < M + 2; i++) {
+		value *= 2;
+		if ((mask >>= 1) == 0) {
+			buffer = (ip >= srcend) ? 0 : *(ip++);
+			mask = 128;
+		}
+		value += ((buffer & mask) != 0);
+	}
+	StartModel();
+	for (i = 0; i < N - F; i++) text_buf[i] = ' ';
+	r = N - F;
+	while (op < dstend) {
+		range = high - low;
+		sym = BinarySearchSym((unsigned long)
+				(((value - low + 1) * sym_cum[0] - 1) / range));
+		high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
+		low +=       (range * sym_cum[sym    ]) / sym_cum[0];
+		for ( ; ; ) {
+			if (low >= Q2) {
+				value -= Q2;  low -= Q2;  high -= Q2;
+			} else if (low >= Q1 && high <= Q3) {
+				value -= Q1;  low -= Q1;  high -= Q1;
+			} else if (high > Q2) break;
+			low += low;  high += high;
+			value *= 2;
+			if ((mask >>= 1) == 0) {
+				buffer = (ip >= srcend) ? 0 : *(ip++);
+				mask = 128;
+			}
+			value += ((buffer & mask) != 0);
+		}
+		c = sym_to_char[sym];
+		UpdateModel(sym);
+		if (c < 256) {
+			if (op >= dstend) return -1;
+			*(op++) = c;
+			text_buf[r++] = c;
+			r &= (N - 1);
+		} else {
+			j = c - 255 + THRESHOLD;
+			range = high - low;
+			i = BinarySearchPos((unsigned long)
+				(((value - low + 1) * position_cum[0] - 1) / range));
+			high = low + (range * position_cum[i    ]) / position_cum[0];
+			low +=       (range * position_cum[i + 1]) / position_cum[0];
+			for ( ; ; ) {
+				if (low >= Q2) {
+					value -= Q2;  low -= Q2;  high -= Q2;
+				} else if (low >= Q1 && high <= Q3) {
+					value -= Q1;  low -= Q1;  high -= Q1;
+				} else if (high > Q2) break;
+				low += low;  high += high;
+				value *= 2;
+				if ((mask >>= 1) == 0) {
+					buffer = (ip >= srcend) ? 0 : *(ip++);
+					mask = 128;
+				}
+				value += ((buffer & mask) != 0);
+			}
+			i = (r - i - 1) & (N - 1);
+			for (k = 0; k < j; k++) {
+				c = text_buf[(i + k) & (N - 1)];
+				if (op >= dstend) return -1;
+				*(op++) = c;
+				text_buf[r++] = c;
+				r &= (N - 1);
+			}		
+		}
+	}
+	return 0;
+}
+
+/* interface to jffs2 follows */
+
+#include "compr.h"
+#include <linux/jffs2.h>
+
+int jffs2_lzari_compress (unsigned char *input,
+			unsigned char *output, uint32_t *sourcelen,
+			uint32_t *dstlen, void *model);
+
+int jffs2_lzari_decompress (unsigned char *input,
+			  unsigned char *output, uint32_t sourcelen,
+			  uint32_t dstlen, void *model);
+
+struct jffs2_compressor jffs2_lzari_comp = {
+	.priority = JFFS2_LZARI_PRIORITY,
+	.name = "lzari",
+	.compr = JFFS2_COMPR_LZARI,
+	.compress = &jffs2_lzari_compress,
+	.decompress = &jffs2_lzari_decompress,
+#ifdef JFFS2_LZARI_DISABLED
+	.disabled = 1,
+#else
+	.disabled = 0,
+#endif
+};
+
+int jffs2_lzari_compress (unsigned char *input,
+			unsigned char *output, uint32_t *sourcelen,
+			uint32_t *dstlen, void *model)
+{
+	return Encode(input, output, (unsigned long *)sourcelen, (unsigned long *)dstlen);
+}
+
+int jffs2_lzari_decompress (unsigned char *input,
+			  unsigned char *output, uint32_t sourcelen,
+			  uint32_t dstlen, void *model)
+{
+    return Decode(input, output, sourcelen, dstlen);
+}
+
+int jffs2_lzari_init (void)
+{
+    return jffs2_register_compressor(&jffs2_lzari_comp);
+}
+
+void jffs2_lzari_exit (void)
+{
+    jffs2_unregister_compressor (&jffs2_lzari_comp);
+}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/compr_lzo.c linux-2.4.30/fs/jffs2/compr_lzo.c
--- linux-2.4.30-orig/fs/jffs2/compr_lzo.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/jffs2/compr_lzo.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,2329 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004 Patrik Kluba,
+ *                    University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id: compr_lzo.c,v 1.2 2005/09/28 14:19:41 lel Exp $
+ *
+ */
+
+/*
+   LZO1X-1 (and -999) compression module for jffs2
+   based on the original LZO sources
+*/
+
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+   Original copyright notice follows:
+
+   lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm
+   lzo_ptr.h -- low-level pointer constructs
+   lzo_swd.ch -- sliding window dictionary
+   lzoconf.h -- configuration for the LZO real-time data compression library
+   lzo_mchw.ch -- matching functions using a window
+   minilzo.c -- mini subset of the LZO real-time data compression library
+   config1x.h -- configuration for the LZO1X algorithm
+   lzo1x.h -- public interface of the LZO1X compression algorithm
+
+   These files are part of the LZO real-time data compression library.
+
+   Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   The LZO library 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.
+
+   The LZO library 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 the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   <markus@oberhumer.com>
+*/
+
+/*
+
+	2004-02-16  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
+				Initial release
+					-removed all 16 bit code
+					-all sensitive data will be on 4 byte boundary
+					-removed check parts for library use
+					-removed all but LZO1X-* compression
+					
+*/
+
+#ifndef __KERNEL__
+  #include <sys/types.h>
+  #include <stddef.h>
+  #include <string.h>
+  #include <limits.h>
+#else
+  #include <linux/kernel.h>
+  #include <linux/types.h>
+  #include <linux/stddef.h>
+  #include <linux/string.h>
+  #define USHRT_MAX     65535
+  /* #define UINT_MAX      4294967295U */
+#endif
+
+/* data type definitions */
+#define U32 unsigned long
+#define S32 signed long
+#define I32 long
+#define U16 unsigned short
+#define S16 signed short
+#define I16 short
+#define U8 unsigned char
+#define S8 signed char
+#define I8 char
+
+/*************************************/
+
+/* lzo_swd.ch */
+
+#define SWD_N				N
+#define SWD_F				F
+#define SWD_THRESHOLD		THRESHOLD
+
+/* shortest unsigned int that 2 * SWD_F + SWD_N (currently 53248) fits in */
+typedef unsigned short swd_uint;
+/* upper limit of that data type */
+#define SWD_UINT_MAX	USHRT_MAX
+
+/* minilzo.c */
+
+#define LZO_VERSION_DATE	"Jul 12 2002"
+#define LZO_VERSION_STRING	"1.08"
+#define LZO_VERSION			0x1080
+
+/* lzo_ptr.h */
+
+/* Integral types that have *exactly* the same number of bits as a lzo_voidp */
+typedef unsigned long lzo_ptr_t;
+typedef long lzo_sptr_t;
+
+
+/*************************************/
+
+/* config1x.h */
+
+#define M1_MAX_OFFSET	0x0400
+#define M2_MAX_OFFSET	0x0800
+#define M3_MAX_OFFSET	0x4000
+#define M4_MAX_OFFSET	0xbfff
+
+#define MX_MAX_OFFSET	(M1_MAX_OFFSET + M2_MAX_OFFSET)
+
+#define M1_MIN_LEN		2
+#define M1_MAX_LEN		2
+#define M2_MIN_LEN		3
+#define M2_MAX_LEN		8
+#define M3_MIN_LEN		3
+#define M3_MAX_LEN		33
+#define M4_MIN_LEN		3
+#define M4_MAX_LEN		9
+
+#define M1_MARKER		0
+#define M2_MARKER		64
+#define M3_MARKER		32
+#define M4_MARKER		16
+
+#define MIN_LOOKAHEAD		(M2_MAX_LEN + 1)
+
+/* minilzo.c */
+
+#define LZO_BYTE(x)       ((unsigned char) ((x) & 0xff))
+
+#define LZO_MAX(a,b)        ((a) >= (b) ? (a) : (b))
+#define LZO_MIN(a,b)        ((a) <= (b) ? (a) : (b))
+#define LZO_MAX3(a,b,c)     ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c))
+#define LZO_MIN3(a,b,c)     ((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c))
+
+#define lzo_sizeof(type)    ((lzo_uint) (sizeof(type)))
+
+#define LZO_HIGH(array)     ((lzo_uint) (sizeof(array)/sizeof(*(array))))
+
+#define LZO_SIZE(bits)      (1u << (bits))
+#define LZO_MASK(bits)      (LZO_SIZE(bits) - 1)
+
+#define LZO_LSIZE(bits)     (1ul << (bits))
+#define LZO_LMASK(bits)     (LZO_LSIZE(bits) - 1)
+
+#define LZO_USIZE(bits)     ((lzo_uint) 1 << (bits))
+#define LZO_UMASK(bits)     (LZO_USIZE(bits) - 1)
+
+#define LZO_STYPE_MAX(b)    (((1l  << (8*(b)-2)) - 1l)  + (1l  << (8*(b)-2)))
+#define LZO_UTYPE_MAX(b)    (((1ul << (8*(b)-1)) - 1ul) + (1ul << (8*(b)-1)))
+
+#define _LZO_STRINGIZE(x)           #x
+#define _LZO_MEXPAND(x)             _LZO_STRINGIZE(x)
+
+#define _LZO_CONCAT2(a,b)           a ## b
+#define _LZO_CONCAT3(a,b,c)         a ## b ## c
+#define _LZO_CONCAT4(a,b,c,d)       a ## b ## c ## d
+#define _LZO_CONCAT5(a,b,c,d,e)     a ## b ## c ## d ## e
+
+#define _LZO_ECONCAT2(a,b)          _LZO_CONCAT2(a,b)
+#define _LZO_ECONCAT3(a,b,c)        _LZO_CONCAT3(a,b,c)
+#define _LZO_ECONCAT4(a,b,c,d)      _LZO_CONCAT4(a,b,c,d)
+#define _LZO_ECONCAT5(a,b,c,d,e)    _LZO_CONCAT5(a,b,c,d,e)
+
+#define lzo_dict_t    const lzo_bytep
+#define lzo_dict_p    lzo_dict_t *
+#define lzo_moff_t    lzo_uint
+
+#define MEMCPY8_DS(dest,src,len) \
+    memcpy(dest,src,len); \
+    dest += len; \
+    src += len
+
+#define MEMCPY_DS(dest,src,len) \
+    do *dest++ = *src++; \
+    while (--len > 0)
+
+#define MEMMOVE_DS(dest,src,len) \
+    do *dest++ = *src++; \
+    while (--len > 0)
+
+#define BZERO8_PTR(s,l,n)   memset((s),0,(lzo_uint)(l)*(n))
+
+#define LZO_BASE 65521u
+#define LZO_NMAX 5552
+
+#define LZO_DO1(buf,i)  {s1 += buf[i]; s2 += s1;}
+#define LZO_DO2(buf,i)  LZO_DO1(buf,i); LZO_DO1(buf,i+1);
+#define LZO_DO4(buf,i)  LZO_DO2(buf,i); LZO_DO2(buf,i+2);
+#define LZO_DO8(buf,i)  LZO_DO4(buf,i); LZO_DO4(buf,i+4);
+#define LZO_DO16(buf,i) LZO_DO8(buf,i); LZO_DO8(buf,i+8);
+
+#define IS_SIGNED(type)       (((type) (-1)) < ((type) 0))
+#define IS_UNSIGNED(type)     (((type) (-1)) > ((type) 0))
+
+#define IS_POWER_OF_2(x)        (((x) & ((x) - 1)) == 0)
+
+#define D_BITS          14
+#define D_INDEX1(d,p)       d = DM((0x21*DX3(p,5,5,6)) >> 5)
+#define D_INDEX2(d,p)       d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f)
+
+#define LZO_HASH            LZO_HASH_LZO_INCREMENTAL_B
+
+#define DL_MIN_LEN          M2_MIN_LEN
+
+#define D_SIZE        LZO_SIZE(D_BITS)
+#define D_MASK        LZO_MASK(D_BITS)
+
+#define D_HIGH        ((D_MASK >> 1) + 1)
+
+#define DINDEX1             D_INDEX1
+#define DINDEX2             D_INDEX2
+
+#define DX2(p,s1,s2) \
+	(((((lzo_uint32)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0])
+
+#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0])
+#define DMS(v,s)        ((lzo_uint) (((v) & (D_MASK >> (s))) << (s)))
+#define DM(v)           DMS(v,0)
+
+#define DENTRY(p,in)                          (p)
+#define GINDEX(m_pos,m_off,dict,dindex,in)    m_pos = dict[dindex]
+
+#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \
+	(m_pos == NULL || (m_off = (lzo_moff_t) (ip - m_pos)) > max_offset)
+
+#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \
+    (BOUNDS_CHECKING_OFF_IN_EXPR( \
+	(PTR_LT(m_pos,in) || \
+	 (m_off = (lzo_moff_t) PTR_DIFF(ip,m_pos)) <= 0 || \
+	  m_off > max_offset) ))
+
+#define BOUNDS_CHECKING_OFF_IN_EXPR(expr)     (expr)
+
+#define DD_BITS			0
+#define DD_SIZE         LZO_SIZE(DD_BITS)
+#define DD_MASK         LZO_MASK(DD_BITS)
+
+#define DL_BITS        (D_BITS - DD_BITS)
+#define DL_SIZE        LZO_SIZE(DL_BITS)
+#define DL_MASK        LZO_MASK(DL_BITS)
+
+#define UPDATE_D(dict,drun,dv,p,in)       dict[ DINDEX(dv,p) ] = DENTRY(p,in)
+#define UPDATE_I(dict,drun,index,p,in)    dict[index] = DENTRY(p,in)
+#define UPDATE_P(ptr,drun,p,in)           (ptr)[0] = DENTRY(p,in)
+
+#define __COPY4(dst,src)  * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src)
+#define COPY4(dst,src)	__COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src))
+
+#define TEST_IP         (ip < ip_end)
+#define TEST_OP         (op <= op_end)
+
+#define NEED_IP(x) \
+            if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x))  goto input_overrun
+#define NEED_OP(x) \
+            if ((lzo_uint)(op_end - op) < (lzo_uint)(x))  goto output_overrun
+#define TEST_LOOKBEHIND(m_pos,out)    if (m_pos < out) goto lookbehind_overrun
+
+/* lzo1x_9x.c */
+
+#define LZO_UINT_MAX	UINT_MAX
+#define N			M4_MAX_OFFSET
+#define THRESHOLD	    1
+#define F		     2048
+
+#define SWD_BEST_OFF	(LZO_MAX3( M2_MAX_LEN, M3_MAX_LEN, M4_MAX_LEN ) + 1)
+
+/* ../include/lzoconf.h */
+
+typedef U32 lzo_uint32;
+typedef I32 lzo_int32;
+typedef U32 lzo_uint;
+typedef I32 lzo_int;
+typedef int lzo_bool;
+
+#define lzo_byte                U8
+#define lzo_bytep               U8 *
+#define lzo_charp               char *
+#define lzo_voidp               void *
+#define lzo_shortp              short *
+#define lzo_ushortp             unsigned short *
+#define lzo_uint32p             lzo_uint32 *
+#define lzo_int32p              lzo_int32 *
+#define lzo_uintp               lzo_uint *
+#define lzo_intp                lzo_int *
+#define lzo_voidpp              lzo_voidp *
+#define lzo_bytepp              lzo_bytep *
+#define lzo_sizeof_dict_t		sizeof(lzo_bytep)
+
+#define LZO_E_OK                    0
+#define LZO_E_ERROR                 (-1)
+#define LZO_E_OUT_OF_MEMORY         (-2)	/* not used right now */
+#define LZO_E_NOT_COMPRESSIBLE      (-3)	/* not used right now */
+#define LZO_E_INPUT_OVERRUN         (-4)
+#define LZO_E_OUTPUT_OVERRUN        (-5)
+#define LZO_E_LOOKBEHIND_OVERRUN    (-6)
+#define LZO_E_EOF_NOT_FOUND         (-7)
+#define LZO_E_INPUT_NOT_CONSUMED    (-8)
+
+#define LZO_PTR_ALIGN_UP(_ptr,_size) \
+   ((_ptr) + (lzo_uint) __lzo_align_gap((const lzo_voidp)(_ptr),(lzo_uint)(_size)))
+#define LZO_ALIGN(_ptr,_size) LZO_PTR_ALIGN_UP(_ptr,_size)
+
+typedef int
+	(*lzo_compress_t) (const lzo_byte * src, lzo_uint src_len,
+			   lzo_byte * dst, lzo_uintp dst_len,
+			   lzo_voidp wrkmem);
+
+typedef int
+	(*lzo_decompress_t) (const lzo_byte * src, lzo_uint src_len,
+			     lzo_byte * dst, lzo_uintp dst_len,
+			     lzo_voidp wrkmem);
+
+typedef int
+	(*lzo_optimize_t) (lzo_byte * src, lzo_uint src_len,
+			   lzo_byte * dst, lzo_uintp dst_len,
+			   lzo_voidp wrkmem);
+
+typedef int
+	(*lzo_compress_dict_t) (const lzo_byte * src, lzo_uint src_len,
+				lzo_byte * dst, lzo_uintp dst_len,
+				lzo_voidp wrkmem,
+				const lzo_byte * dict, lzo_uint dict_len);
+
+typedef int
+	(*lzo_decompress_dict_t) (const lzo_byte * src, lzo_uint src_len,
+				  lzo_byte * dst, lzo_uintp dst_len,
+				  lzo_voidp wrkmem,
+				  const lzo_byte * dict, lzo_uint dict_len);
+
+typedef int
+	(*lzo_compress_asm_t) (const lzo_byte * src, lzo_uint src_len,
+			       lzo_byte * dst, lzo_uintp dst_len,
+			       lzo_voidp wrkmem);
+
+typedef int
+	(*lzo_decompress_asm_t) (const lzo_byte * src, lzo_uint src_len,
+				 lzo_byte * dst, lzo_uintp dst_len,
+				 lzo_voidp wrkmem);
+
+typedef void (*lzo_progress_callback_t) (lzo_uint, lzo_uint);
+
+typedef union
+{
+	lzo_bytep p;
+	lzo_uint u;
+} __lzo_pu_u;
+typedef union
+{
+	lzo_bytep p;
+	lzo_uint32 u32;
+} __lzo_pu32_u;
+typedef union
+{
+	void *vp;
+	lzo_bytep bp;
+	lzo_uint32 u32;
+	long l;
+} lzo_align_t;
+
+/* lzo1x.h */
+
+#define LZO1X_1_MEM_COMPRESS    ((lzo_uint32) (16384L * lzo_sizeof_dict_t))
+#define LZO1X_999_MEM_COMPRESS  ((lzo_uint32) (14 * 16384L * sizeof(short)))
+
+/* lzo_ptr.h */
+
+#define PTR(a)				((lzo_ptr_t) (a))
+#define PTR_LINEAR(a)		PTR(a)
+#define PTR_ALIGNED_4(a)	((PTR_LINEAR(a) & 3) == 0)
+#define PTR_ALIGNED_8(a)	((PTR_LINEAR(a) & 7) == 0)
+#define PTR_ALIGNED2_4(a,b)	(((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0)
+#define PTR_ALIGNED2_8(a,b)	(((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0)
+#define PTR_LT(a,b)			(PTR(a) < PTR(b))
+#define PTR_GE(a,b)			(PTR(a) >= PTR(b))
+#define PTR_DIFF(a,b)		((lzo_ptrdiff_t) (PTR(a) - PTR(b)))
+#define pd(a,b)		        ((lzo_uint) ((a)-(b)))
+
+typedef ptrdiff_t lzo_ptrdiff_t;
+
+typedef union
+{
+	char a_char;
+	unsigned char a_uchar;
+	short a_short;
+	unsigned short a_ushort;
+	int a_int;
+	unsigned int a_uint;
+	long a_long;
+	unsigned long a_ulong;
+	lzo_int a_lzo_int;
+	lzo_uint a_lzo_uint;
+	lzo_int32 a_lzo_int32;
+	lzo_uint32 a_lzo_uint32;
+	ptrdiff_t a_ptrdiff_t;
+	lzo_ptrdiff_t a_lzo_ptrdiff_t;
+	lzo_ptr_t a_lzo_ptr_t;
+	lzo_voidp a_lzo_voidp;
+	void *a_void_p;
+	lzo_bytep a_lzo_bytep;
+	lzo_bytepp a_lzo_bytepp;
+	lzo_uintp a_lzo_uintp;
+	lzo_uint *a_lzo_uint_p;
+	lzo_uint32p a_lzo_uint32p;
+	lzo_uint32 *a_lzo_uint32_p;
+	unsigned char *a_uchar_p;
+	char *a_char_p;
+}
+lzo_full_align_t;
+
+/* lzo_mchw.ch */
+
+typedef struct
+{
+	int init;
+
+	lzo_uint look;
+
+	lzo_uint m_len;
+	lzo_uint m_off;
+
+	lzo_uint last_m_len;
+	lzo_uint last_m_off;
+
+	const lzo_byte *bp;
+	const lzo_byte *ip;
+	const lzo_byte *in;
+	const lzo_byte *in_end;
+	lzo_byte *out;
+
+	lzo_progress_callback_t cb;
+
+	lzo_uint textsize;
+	lzo_uint codesize;
+	lzo_uint printcount;
+
+	unsigned long lit_bytes;
+	unsigned long match_bytes;
+	unsigned long rep_bytes;
+	unsigned long lazy;
+
+	lzo_uint r1_lit;
+	lzo_uint r1_m_len;
+
+	unsigned long m1a_m, m1b_m, m2_m, m3_m, m4_m;
+	unsigned long lit1_r, lit2_r, lit3_r;
+}
+lzo1x_999_t;
+
+#define getbyte(c) 	((c).ip < (c).in_end ? *((c).ip)++ : (-1))
+
+/* lzo_swd.ch */
+
+#define SWD_UINT(x)			((swd_uint)(x))
+#define SWD_HSIZE			16384
+#define SWD_MAX_CHAIN		2048
+#define HEAD3(b,p) \
+	(((0x9f5f*(((((lzo_uint32)b[p]<<5)^b[p+1])<<5)^b[p+2]))>>5) & (SWD_HSIZE-1))
+#define HEAD2(b,p)      (b[p] ^ ((unsigned)b[p+1]<<8))
+#define NIL2				SWD_UINT_MAX
+
+typedef struct
+{
+	lzo_uint n;
+	lzo_uint f;
+	lzo_uint threshold;
+
+	lzo_uint max_chain;
+	lzo_uint nice_length;
+	lzo_bool use_best_off;
+	lzo_uint lazy_insert;
+
+	lzo_uint m_len;
+	lzo_uint m_off;
+	lzo_uint look;
+	int b_char;
+
+	lzo_uint best_off[SWD_BEST_OFF];
+
+	lzo1x_999_t *c;
+	lzo_uint m_pos;
+
+	lzo_uint best_pos[SWD_BEST_OFF];
+
+	const lzo_byte *dict;
+	const lzo_byte *dict_end;
+	lzo_uint dict_len;
+
+	lzo_uint ip;
+	lzo_uint bp;
+	lzo_uint rp;
+	lzo_uint b_size;
+
+	unsigned char *b_wrap;
+
+	lzo_uint node_count;
+	lzo_uint first_rp;
+
+	unsigned char b[SWD_N + SWD_F + SWD_F];
+	swd_uint head3[SWD_HSIZE];
+	swd_uint succ3[SWD_N + SWD_F];
+	swd_uint best3[SWD_N + SWD_F];
+	swd_uint llen3[SWD_HSIZE];
+
+	swd_uint head2[65536L];
+}
+lzo1x_999_swd_t;
+
+#define s_head3(s,key)		s->head3[key]
+#define swd_pos2off(s,pos) \
+	(s->bp > (pos) ? s->bp - (pos) : s->b_size - ((pos) - s->bp))
+
+static __inline__ void
+swd_getbyte (lzo1x_999_swd_t * s)
+{
+	int c;
+
+	if ((c = getbyte (*(s->c))) < 0)
+	{
+		if (s->look > 0)
+			--s->look;
+	}
+	else
+	{
+		s->b[s->ip] = LZO_BYTE (c);
+		if (s->ip < s->f)
+			s->b_wrap[s->ip] = LZO_BYTE (c);
+	}
+	if (++s->ip == s->b_size)
+		s->ip = 0;
+	if (++s->bp == s->b_size)
+		s->bp = 0;
+	if (++s->rp == s->b_size)
+		s->rp = 0;
+}
+
+static void
+swd_initdict (lzo1x_999_swd_t * s, const lzo_byte * dict, lzo_uint dict_len)
+{
+	s->dict = s->dict_end = NULL;
+	s->dict_len = 0;
+
+	if (!dict || dict_len <= 0)
+		return;
+	if (dict_len > s->n)
+	{
+		dict += dict_len - s->n;
+		dict_len = s->n;
+	}
+
+	s->dict = dict;
+	s->dict_len = dict_len;
+	s->dict_end = dict + dict_len;
+	memcpy (s->b, dict, dict_len);
+	s->ip = dict_len;
+}
+
+static void
+swd_insertdict (lzo1x_999_swd_t * s, lzo_uint node, lzo_uint len)
+{
+	lzo_uint key;
+
+	s->node_count = s->n - len;
+	s->first_rp = node;
+
+	while (len-- > 0)
+	{
+		key = HEAD3 (s->b, node);
+		s->succ3[node] = s_head3 (s, key);
+		s->head3[key] = SWD_UINT (node);
+		s->best3[node] = SWD_UINT (s->f + 1);
+		s->llen3[key]++;
+
+		key = HEAD2 (s->b, node);
+		s->head2[key] = SWD_UINT (node);
+
+		node++;
+	}
+}
+
+static int
+swd_init (lzo1x_999_swd_t * s, const lzo_byte * dict, lzo_uint dict_len)
+{
+
+	s->n = SWD_N;
+	s->f = SWD_F;
+	s->threshold = SWD_THRESHOLD;
+
+
+
+	s->max_chain = SWD_MAX_CHAIN;
+	s->nice_length = SWD_F;
+	s->use_best_off = 0;
+	s->lazy_insert = 0;
+
+	s->b_size = s->n + s->f;
+	if (2 * s->f >= s->n || s->b_size + s->f >= NIL2)
+		return LZO_E_ERROR;
+	s->b_wrap = s->b + s->b_size;
+	s->node_count = s->n;
+
+	memset (s->llen3, 0, sizeof (s->llen3[0]) * SWD_HSIZE);
+	memset (s->head2, 0xff, sizeof (s->head2[0]) * 65536L);
+
+	s->ip = 0;
+	swd_initdict (s, dict, dict_len);
+	s->bp = s->ip;
+	s->first_rp = s->ip;
+
+	s->look = (lzo_uint) (s->c->in_end - s->c->ip);
+	if (s->look > 0)
+	{
+		if (s->look > s->f)
+			s->look = s->f;
+		memcpy (&s->b[s->ip], s->c->ip, s->look);
+		s->c->ip += s->look;
+		s->ip += s->look;
+	}
+
+	if (s->ip == s->b_size)
+		s->ip = 0;
+
+	if (s->look >= 2 && s->dict_len > 0)
+		swd_insertdict (s, 0, s->dict_len);
+
+	s->rp = s->first_rp;
+	if (s->rp >= s->node_count)
+		s->rp -= s->node_count;
+	else
+		s->rp += s->b_size - s->node_count;
+
+	return LZO_E_OK;
+}
+
+static __inline__ void
+swd_remove_node (lzo1x_999_swd_t * s, lzo_uint node)
+{
+	if (s->node_count == 0)
+	{
+		lzo_uint key;
+
+		key = HEAD3 (s->b, node);
+
+		--s->llen3[key];
+
+		key = HEAD2 (s->b, node);
+
+		if ((lzo_uint) s->head2[key] == node)
+			s->head2[key] = NIL2;
+	}
+	else
+		--s->node_count;
+}
+
+static void
+swd_accept (lzo1x_999_swd_t * s, lzo_uint n)
+{
+
+	while (n--)
+	{
+		lzo_uint key;
+
+		swd_remove_node (s, s->rp);
+
+		key = HEAD3 (s->b, s->bp);
+		s->succ3[s->bp] = s_head3 (s, key);
+		s->head3[key] = SWD_UINT (s->bp);
+		s->best3[s->bp] = SWD_UINT (s->f + 1);
+		s->llen3[key]++;
+
+		key = HEAD2 (s->b, s->bp);
+		s->head2[key] = SWD_UINT (s->bp);;
+
+		swd_getbyte (s);
+	}
+}
+
+static void
+swd_search (lzo1x_999_swd_t * s, lzo_uint node, lzo_uint cnt)
+{
+	const unsigned char *p1;
+	const unsigned char *p2;
+	const unsigned char *px;
+
+	lzo_uint m_len = s->m_len;
+	const unsigned char *b = s->b;
+	const unsigned char *bp = s->b + s->bp;
+	const unsigned char *bx = s->b + s->bp + s->look;
+	unsigned char scan_end1;
+
+	scan_end1 = bp[m_len - 1];
+	for (; cnt-- > 0; node = s->succ3[node])
+	{
+		p1 = bp;
+		p2 = b + node;
+		px = bx;
+
+		if (p2[m_len - 1] == scan_end1 &&
+		    p2[m_len] == p1[m_len] &&
+		    p2[0] == p1[0] && p2[1] == p1[1])
+		{
+			lzo_uint i;
+
+			p1 += 2;
+			p2 += 2;
+			do
+			{
+			}
+			while (++p1 < px && *p1 == *++p2);
+
+			i = p1 - bp;
+
+			if (i < SWD_BEST_OFF)
+			{
+				if (s->best_pos[i] == 0)
+					s->best_pos[i] = node + 1;
+			}
+
+			if (i > m_len)
+			{
+				s->m_len = m_len = i;
+				s->m_pos = node;
+				if (m_len == s->look)
+					return;
+				if (m_len >= s->nice_length)
+					return;
+				if (m_len > (lzo_uint) s->best3[node])
+					return;
+				scan_end1 = bp[m_len - 1];
+			}
+		}
+	}
+}
+
+static lzo_bool
+swd_search2 (lzo1x_999_swd_t * s)
+{
+	lzo_uint key;
+
+	key = s->head2[HEAD2 (s->b, s->bp)];
+	if (key == NIL2)
+		return 0;
+
+	if (s->best_pos[2] == 0)
+		s->best_pos[2] = key + 1;
+
+	if (s->m_len < 2)
+	{
+		s->m_len = 2;
+		s->m_pos = key;
+	}
+	return 1;
+}
+
+static void
+swd_findbest (lzo1x_999_swd_t * s)
+{
+	lzo_uint key;
+	lzo_uint cnt, node;
+	lzo_uint len;
+
+	key = HEAD3 (s->b, s->bp);
+	node = s->succ3[s->bp] = s_head3 (s, key);
+	cnt = s->llen3[key]++;
+
+	if (cnt > s->max_chain && s->max_chain > 0)
+		cnt = s->max_chain;
+	s->head3[key] = SWD_UINT (s->bp);
+
+	s->b_char = s->b[s->bp];
+	len = s->m_len;
+	if (s->m_len >= s->look)
+	{
+		if (s->look == 0)
+			s->b_char = -1;
+		s->m_off = 0;
+		s->best3[s->bp] = SWD_UINT (s->f + 1);
+	}
+	else
+	{
+
+		if (swd_search2 (s))
+
+			if (s->look >= 3)
+				swd_search (s, node, cnt);
+		if (s->m_len > len)
+			s->m_off = swd_pos2off (s, s->m_pos);
+		s->best3[s->bp] = SWD_UINT (s->m_len);
+
+		if (s->use_best_off)
+		{
+			int i;
+			for (i = 2; i < SWD_BEST_OFF; i++)
+				if (s->best_pos[i] > 0)
+					s->best_off[i] =
+						swd_pos2off (s,
+							     s->best_pos[i] -
+							     1);
+				else
+					s->best_off[i] = 0;
+		}
+
+	}
+
+	swd_remove_node (s, s->rp);
+
+	key = HEAD2 (s->b, s->bp);
+	s->head2[key] = SWD_UINT (s->bp);
+
+}
+
+/* lzo_mchw.ch */
+
+static int
+init_match (lzo1x_999_t * c, lzo1x_999_swd_t * s,
+	    const lzo_byte * dict, lzo_uint dict_len, lzo_uint32 flags)
+{
+	int r;
+
+	c->init = 1;
+
+	s->c = c;
+
+	c->last_m_len = c->last_m_off = 0;
+
+	c->textsize = c->codesize = c->printcount = 0;
+	c->lit_bytes = c->match_bytes = c->rep_bytes = 0;
+	c->lazy = 0;
+
+	r = swd_init (s, dict, dict_len);
+	if (r != 0)
+		return r;
+
+	s->use_best_off = (flags & 1) ? 1 : 0;
+	return r;
+}
+
+static int
+find_match (lzo1x_999_t * c, lzo1x_999_swd_t * s,
+	    lzo_uint this_len, lzo_uint skip)
+{
+	if (skip > 0)
+	{
+		swd_accept (s, this_len - skip);
+		c->textsize += this_len - skip + 1;
+	}
+	else
+	{
+		c->textsize += this_len - skip;
+	}
+
+	s->m_len = 1;
+	s->m_len = 1;
+
+	if (s->use_best_off)
+		memset (s->best_pos, 0, sizeof (s->best_pos));
+
+	swd_findbest (s);
+	c->m_len = s->m_len;
+	c->m_off = s->m_off;
+
+	swd_getbyte (s);
+
+	if (s->b_char < 0)
+	{
+		c->look = 0;
+		c->m_len = 0;
+	}
+	else
+	{
+		c->look = s->look + 1;
+	}
+	c->bp = c->ip - c->look;
+
+	if (c->cb && c->textsize > c->printcount)
+	{
+		(*c->cb) (c->textsize, c->codesize);
+		c->printcount += 1024;
+	}
+
+	return LZO_E_OK;
+}
+
+/* lzo1x_9x.c */
+
+static lzo_byte *
+code_match (lzo1x_999_t * c, lzo_byte * op, lzo_uint m_len, lzo_uint m_off)
+{
+	lzo_uint x_len = m_len;
+	lzo_uint x_off = m_off;
+
+	c->match_bytes += m_len;
+
+	if (m_len == 2)
+	{
+		m_off -= 1;
+
+		*op++ = LZO_BYTE (M1_MARKER | ((m_off & 3) << 2));
+		*op++ = LZO_BYTE (m_off >> 2);
+
+		c->m1a_m++;
+	}
+
+	else if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET)
+
+	{
+
+		m_off -= 1;
+		*op++ = LZO_BYTE (((m_len - 1) << 5) | ((m_off & 7) << 2));
+		*op++ = LZO_BYTE (m_off >> 3);
+		c->m2_m++;
+	}
+	else if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET
+		 && c->r1_lit >= 4)
+	{
+		m_off -= 1 + M2_MAX_OFFSET;
+
+		*op++ = LZO_BYTE (M1_MARKER | ((m_off & 3) << 2));
+		*op++ = LZO_BYTE (m_off >> 2);
+
+		c->m1b_m++;
+	}
+	else if (m_off <= M3_MAX_OFFSET)
+	{
+		m_off -= 1;
+		if (m_len <= M3_MAX_LEN)
+			*op++ = LZO_BYTE (M3_MARKER | (m_len - 2));
+		else
+		{
+			m_len -= M3_MAX_LEN;
+			*op++ = M3_MARKER | 0;
+			while (m_len > 255)
+			{
+				m_len -= 255;
+				*op++ = 0;
+			}
+			*op++ = LZO_BYTE (m_len);
+		}
+
+		*op++ = LZO_BYTE (m_off << 2);
+		*op++ = LZO_BYTE (m_off >> 6);
+
+		c->m3_m++;
+	}
+	else
+	{
+		lzo_uint k;
+
+		m_off -= 0x4000;
+		k = (m_off & 0x4000) >> 11;
+		if (m_len <= M4_MAX_LEN)
+			*op++ = LZO_BYTE (M4_MARKER | k | (m_len - 2));
+		else
+		{
+			m_len -= M4_MAX_LEN;
+			*op++ = LZO_BYTE (M4_MARKER | k | 0);
+			while (m_len > 255)
+			{
+				m_len -= 255;
+				*op++ = 0;
+			}
+			*op++ = LZO_BYTE (m_len);
+		}
+
+		*op++ = LZO_BYTE (m_off << 2);
+		*op++ = LZO_BYTE (m_off >> 6);
+
+		c->m4_m++;
+	}
+
+	c->last_m_len = x_len;
+	c->last_m_off = x_off;
+	return op;
+}
+
+static lzo_byte *
+STORE_RUN (lzo1x_999_t * c, lzo_byte * op, const lzo_byte * ii, lzo_uint t)
+{
+	c->lit_bytes += t;
+
+	if (op == c->out && t <= 238)
+	{
+		*op++ = LZO_BYTE (17 + t);
+	}
+	else if (t <= 3)
+	{
+		op[-2] |= LZO_BYTE (t);
+
+		c->lit1_r++;
+	}
+	else if (t <= 18)
+	{
+		*op++ = LZO_BYTE (t - 3);
+		c->lit2_r++;
+	}
+	else
+	{
+		lzo_uint tt = t - 18;
+
+		*op++ = 0;
+		while (tt > 255)
+		{
+			tt -= 255;
+			*op++ = 0;
+		}
+		*op++ = LZO_BYTE (tt);
+		c->lit3_r++;
+	}
+	do
+		*op++ = *ii++;
+	while (--t > 0);
+
+	return op;
+}
+
+static lzo_byte *
+code_run (lzo1x_999_t * c, lzo_byte * op, const lzo_byte * ii,
+	  lzo_uint lit, lzo_uint m_len)
+{
+	if (lit > 0)
+	{
+		op = STORE_RUN (c, op, ii, lit);
+		c->r1_m_len = m_len;
+		c->r1_lit = lit;
+	}
+	else
+	{
+		c->r1_m_len = 0;
+		c->r1_lit = 0;
+	}
+
+	return op;
+}
+
+static int
+len_of_coded_match (lzo_uint m_len, lzo_uint m_off, lzo_uint lit)
+{
+	int n = 4;
+
+	if (m_len < 2)
+		return -1;
+	if (m_len == 2)
+		return (m_off <= M1_MAX_OFFSET && lit > 0
+			&& lit < 4) ? 2 : -1;
+	if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET)
+		return 2;
+	if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && lit >= 4)
+		return 2;
+	if (m_off <= M3_MAX_OFFSET)
+	{
+		if (m_len <= M3_MAX_LEN)
+			return 3;
+		m_len -= M3_MAX_LEN;
+		while (m_len > 255)
+		{
+			m_len -= 255;
+			n++;
+		}
+		return n;
+	}
+	if (m_off <= M4_MAX_OFFSET)
+	{
+		if (m_len <= M4_MAX_LEN)
+			return 3;
+		m_len -= M4_MAX_LEN;
+		while (m_len > 255)
+		{
+			m_len -= 255;
+			n++;
+		}
+		return n;
+	}
+	return -1;
+}
+
+static lzo_int
+min_gain (lzo_uint ahead, lzo_uint lit1, lzo_uint lit2, int l1, int l2,
+	  int l3)
+{
+	lzo_int lazy_match_min_gain = 0;
+
+	lazy_match_min_gain += ahead;
+
+	if (lit1 <= 3)
+		lazy_match_min_gain += (lit2 <= 3) ? 0 : 2;
+	else if (lit1 <= 18)
+		lazy_match_min_gain += (lit2 <= 18) ? 0 : 1;
+
+	lazy_match_min_gain += (l2 - l1) * 2;
+	if (l3 > 0)
+		lazy_match_min_gain -= (ahead - l3) * 2;
+
+	if (lazy_match_min_gain < 0)
+		lazy_match_min_gain = 0;
+
+	return lazy_match_min_gain;
+}
+
+static void
+better_match (const lzo1x_999_swd_t * swd, lzo_uint * m_len, lzo_uint * m_off)
+{
+	if (*m_len <= M2_MIN_LEN)
+		return;
+
+	if (*m_off <= M2_MAX_OFFSET)
+		return;
+
+	if (*m_off > M2_MAX_OFFSET &&
+	    *m_len >= M2_MIN_LEN + 1 && *m_len <= M2_MAX_LEN + 1 &&
+	    swd->best_off[*m_len - 1]
+	    && swd->best_off[*m_len - 1] <= M2_MAX_OFFSET)
+	{
+		*m_len = *m_len - 1;
+		*m_off = swd->best_off[*m_len];
+		return;
+	}
+
+	if (*m_off > M3_MAX_OFFSET &&
+	    *m_len >= M4_MAX_LEN + 1 && *m_len <= M2_MAX_LEN + 2 &&
+	    swd->best_off[*m_len - 2]
+	    && swd->best_off[*m_len - 2] <= M2_MAX_OFFSET)
+	{
+		*m_len = *m_len - 2;
+		*m_off = swd->best_off[*m_len];
+		return;
+	}
+
+	if (*m_off > M3_MAX_OFFSET &&
+	    *m_len >= M4_MAX_LEN + 1 && *m_len <= M3_MAX_LEN + 1 &&
+	    swd->best_off[*m_len - 1]
+	    && swd->best_off[*m_len - 1] <= M3_MAX_OFFSET)
+	{
+		*m_len = *m_len - 1;
+		*m_off = swd->best_off[*m_len];
+	}
+
+}
+
+/* minilzo.c */
+
+static lzo_bool
+lzo_assert (int expr)
+{
+	return (expr) ? 1 : 0;
+}
+
+/* lzo1x_9x.c */
+
+static int
+lzo1x_999_compress_internal (const lzo_byte * in, lzo_uint in_len,
+			     lzo_byte * out, lzo_uintp out_len,
+			     lzo_voidp wrkmem,
+			     const lzo_byte * dict, lzo_uint dict_len,
+			     lzo_progress_callback_t cb,
+			     int try_lazy,
+			     lzo_uint good_length,
+			     lzo_uint max_lazy,
+			     lzo_uint nice_length,
+			     lzo_uint max_chain, lzo_uint32 flags)
+{
+	lzo_byte *op;
+	const lzo_byte *ii;
+	lzo_uint lit;
+	lzo_uint m_len, m_off;
+	lzo1x_999_t cc;
+	lzo1x_999_t *const c = &cc;
+	lzo1x_999_swd_t *const swd = (lzo1x_999_swd_t *) wrkmem;
+	int r;
+
+	if (!lzo_assert
+	    (LZO1X_999_MEM_COMPRESS >= lzo_sizeof (lzo1x_999_swd_t)))
+		return LZO_E_ERROR;
+
+	if (try_lazy < 0)
+		try_lazy = 1;
+
+	if (good_length <= 0)
+		good_length = 32;
+
+	if (max_lazy <= 0)
+		max_lazy = 32;
+
+	if (nice_length <= 0)
+		nice_length = 0;
+
+	if (max_chain <= 0)
+		max_chain = SWD_MAX_CHAIN;
+
+	c->init = 0;
+	c->ip = c->in = in;
+	c->in_end = in + in_len;
+	c->out = out;
+	c->cb = cb;
+	c->m1a_m = c->m1b_m = c->m2_m = c->m3_m = c->m4_m = 0;
+	c->lit1_r = c->lit2_r = c->lit3_r = 0;
+
+	op = out;
+	ii = c->ip;
+	lit = 0;
+	c->r1_lit = c->r1_m_len = 0;
+
+	r = init_match (c, swd, dict, dict_len, flags);
+	if (r != 0)
+		return r;
+	if (max_chain > 0)
+		swd->max_chain = max_chain;
+	if (nice_length > 0)
+		swd->nice_length = nice_length;
+
+	r = find_match (c, swd, 0, 0);
+	if (r != 0)
+		return r;
+	while (c->look > 0)
+	{
+		lzo_uint ahead;
+		lzo_uint max_ahead;
+		int l1, l2, l3;
+
+		c->codesize = op - out;
+
+		m_len = c->m_len;
+		m_off = c->m_off;
+
+		if (lit == 0)
+			ii = c->bp;
+
+		if (m_len < 2 ||
+		    (m_len == 2
+		     && (m_off > M1_MAX_OFFSET || lit == 0 || lit >= 4))
+		    || (m_len == 2 && op == out) || (op == out && lit == 0))
+		{
+
+			m_len = 0;
+		}
+		else if (m_len == M2_MIN_LEN)
+		{
+
+			if (m_off > MX_MAX_OFFSET && lit >= 4)
+				m_len = 0;
+		}
+
+		if (m_len == 0)
+		{
+
+			lit++;
+			swd->max_chain = max_chain;
+			r = find_match (c, swd, 1, 0);
+			continue;
+		}
+
+		if (swd->use_best_off)
+			better_match (swd, &m_len, &m_off);
+
+		ahead = 0;
+		if (try_lazy <= 0 || m_len >= max_lazy)
+		{
+
+			l1 = 0;
+			max_ahead = 0;
+		}
+		else
+		{
+
+			l1 = len_of_coded_match (m_len, m_off, lit);
+
+			max_ahead = LZO_MIN (try_lazy, l1 - 1);
+
+		}
+
+		while (ahead < max_ahead && c->look > m_len)
+		{
+			lzo_int lazy_match_min_gain;
+
+			if (m_len >= good_length)
+				swd->max_chain = max_chain >> 2;
+			else
+				swd->max_chain = max_chain;
+			r = find_match (c, swd, 1, 0);
+			ahead++;
+
+			if (c->m_len < m_len)
+				continue;
+
+			if (c->m_len == m_len && c->m_off >= m_off)
+				continue;
+
+			if (swd->use_best_off)
+				better_match (swd, &c->m_len, &c->m_off);
+
+			l2 = len_of_coded_match (c->m_len, c->m_off,
+						 lit + ahead);
+			if (l2 < 0)
+				continue;
+
+			l3 = (op == out) ? -1 : len_of_coded_match (ahead,
+								    m_off,
+								    lit);
+
+			lazy_match_min_gain =
+				min_gain (ahead, lit, lit + ahead, l1, l2,
+					  l3);
+			if (c->m_len >= m_len + lazy_match_min_gain)
+			{
+				c->lazy++;
+
+				if (l3 > 0)
+				{
+
+					op = code_run (c, op, ii, lit, ahead);
+					lit = 0;
+
+					op = code_match (c, op, ahead, m_off);
+				}
+				else
+				{
+					lit += ahead;
+				}
+				goto lazy_match_done;
+			}
+		}
+
+		op = code_run (c, op, ii, lit, m_len);
+		lit = 0;
+
+		op = code_match (c, op, m_len, m_off);
+		swd->max_chain = max_chain;
+		r = find_match (c, swd, m_len, 1 + ahead);
+
+	      lazy_match_done:;
+	}
+
+	if (lit > 0)
+		op = STORE_RUN (c, op, ii, lit);
+
+	*op++ = M4_MARKER | 1;
+	*op++ = 0;
+	*op++ = 0;
+
+	c->codesize = op - out;
+
+	*out_len = op - out;
+
+	if (c->cb)
+		(*c->cb) (c->textsize, c->codesize);
+
+	return LZO_E_OK;
+}
+
+static int
+lzo1x_999_compress_level (const lzo_byte * in, lzo_uint in_len,
+			  lzo_byte * out, lzo_uintp out_len,
+			  lzo_voidp wrkmem,
+			  const lzo_byte * dict, lzo_uint dict_len,
+			  lzo_progress_callback_t cb, int compression_level)
+{
+	static const struct
+	{
+		int try_lazy;
+		lzo_uint good_length;
+		lzo_uint max_lazy;
+		lzo_uint nice_length;
+		lzo_uint max_chain;
+		lzo_uint32 flags;
+	} c[9] =
+	{
+		{
+		0, 0, 0, 8, 4, 0},
+		{
+		0, 0, 0, 16, 8, 0},
+		{
+		0, 0, 0, 32, 16, 0},
+		{
+		1, 4, 4, 16, 16, 0},
+		{
+		1, 8, 16, 32, 32, 0},
+		{
+		1, 8, 16, 128, 128, 0},
+		{
+		2, 8, 32, 128, 256, 0},
+		{
+		2, 32, 128, F, 2048, 1},
+		{
+		2, F, F, F, 4096, 1}
+	};
+
+	if (compression_level < 1 || compression_level > 9)
+		return LZO_E_ERROR;
+
+	compression_level -= 1;
+	return lzo1x_999_compress_internal (in, in_len, out, out_len, wrkmem,
+					    dict, dict_len, cb,
+					    c[compression_level].try_lazy,
+					    c[compression_level].good_length,
+					    c[compression_level].max_lazy,
+					    0,
+					    c[compression_level].max_chain,
+					    c[compression_level].flags);
+}
+
+static int
+lzo1x_999_compress (const lzo_byte * in, lzo_uint in_len,
+		    lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+	return lzo1x_999_compress_level (in, in_len, out, out_len, wrkmem,
+					 NULL, 0, 0, 8);
+}
+
+/* minilzo.c */
+
+#ifdef JFFS2_LZO_1
+static const lzo_byte __lzo_copyright[] = LZO_VERSION_STRING;
+
+static lzo_uint
+_lzo1x_1_do_compress (const lzo_byte * in, lzo_uint in_len,
+		      lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+
+	register const lzo_byte *ip;
+
+	lzo_byte *op;
+	const lzo_byte *const in_end = in + in_len;
+	const lzo_byte *const ip_end = in + in_len - 8 - 5;
+	const lzo_byte *ii;
+	lzo_dict_p const dict = (lzo_dict_p) wrkmem;
+
+	op = out;
+	ip = in;
+	ii = ip;
+
+	ip += 4;
+	for (;;)
+	{
+		register const lzo_byte *m_pos;
+
+		lzo_uint m_off;
+		lzo_uint m_len;
+		lzo_uint dindex;
+
+		DINDEX1 (dindex, ip);
+		GINDEX (m_pos, m_off, dict, dindex, in);
+		if (LZO_CHECK_MPOS_NON_DET
+		    (m_pos, m_off, in, ip, M4_MAX_OFFSET))
+			goto literal;
+
+		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
+			goto try_match;
+		DINDEX2 (dindex, ip);
+		GINDEX (m_pos, m_off, dict, dindex, in);
+
+		if (LZO_CHECK_MPOS_NON_DET
+		    (m_pos, m_off, in, ip, M4_MAX_OFFSET))
+			goto literal;
+		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
+			goto try_match;
+		goto literal;
+
+	      try_match:
+		if (m_pos[0] != ip[0] || m_pos[1] != ip[1])
+		{
+		}
+		else
+		{
+			if (m_pos[2] == ip[2])
+			{
+				goto match;
+			}
+			else
+			{
+			}
+		}
+
+	      literal:
+		UPDATE_I (dict, 0, dindex, ip, in);
+		++ip;
+		if (ip >= ip_end)
+			break;
+		continue;
+
+	      match:
+		UPDATE_I (dict, 0, dindex, ip, in);
+
+		if (pd (ip, ii) > 0)
+		{
+			register lzo_uint t = pd (ip, ii);
+
+			if (t <= 3)
+			{
+				op[-2] |= LZO_BYTE (t);
+			}
+			else if (t <= 18)
+				*op++ = LZO_BYTE (t - 3);
+			else
+			{
+				register lzo_uint tt = t - 18;
+
+				*op++ = 0;
+				while (tt > 255)
+				{
+					tt -= 255;
+					*op++ = 0;
+				}
+				*op++ = LZO_BYTE (tt);;
+			}
+			do
+				*op++ = *ii++;
+			while (--t > 0);
+		}
+
+		ip += 3;
+		if (m_pos[3] != *ip++ || m_pos[4] != *ip++
+		    || m_pos[5] != *ip++ || m_pos[6] != *ip++
+		    || m_pos[7] != *ip++ || m_pos[8] != *ip++)
+		{
+			--ip;
+			m_len = ip - ii;
+
+			if (m_off <= M2_MAX_OFFSET)
+			{
+				m_off -= 1;
+
+				*op++ = LZO_BYTE (((m_len -
+						    1) << 5) | ((m_off & 7) <<
+								2));
+				*op++ = LZO_BYTE (m_off >> 3);
+			}
+			else if (m_off <= M3_MAX_OFFSET)
+			{
+				m_off -= 1;
+				*op++ = LZO_BYTE (M3_MARKER | (m_len - 2));
+				goto m3_m4_offset;
+			}
+			else
+
+			{
+				m_off -= 0x4000;
+
+				*op++ = LZO_BYTE (M4_MARKER |
+						  ((m_off & 0x4000) >> 11) |
+						  (m_len - 2));
+				goto m3_m4_offset;
+			}
+		}
+		else
+		{
+			{
+				const lzo_byte *end = in_end;
+				const lzo_byte *m = m_pos + M2_MAX_LEN + 1;
+				while (ip < end && *m == *ip)
+					m++, ip++;
+				m_len = (ip - ii);
+			}
+
+
+			if (m_off <= M3_MAX_OFFSET)
+			{
+				m_off -= 1;
+				if (m_len <= 33)
+					*op++ = LZO_BYTE (M3_MARKER |
+							  (m_len - 2));
+				else
+				{
+					m_len -= 33;
+					*op++ = M3_MARKER | 0;
+					goto m3_m4_len;
+				}
+			}
+			else
+			{
+				m_off -= 0x4000;
+
+				if (m_len <= M4_MAX_LEN)
+					*op++ = LZO_BYTE (M4_MARKER |
+							  ((m_off & 0x4000) >>
+							   11) | (m_len - 2));
+
+				else
+				{
+					m_len -= M4_MAX_LEN;
+					*op++ = LZO_BYTE (M4_MARKER |
+							  ((m_off & 0x4000) >>
+							   11));
+				      m3_m4_len:
+					while (m_len > 255)
+					{
+						m_len -= 255;
+						*op++ = 0;
+					}
+
+					*op++ = LZO_BYTE (m_len);
+				}
+			}
+
+		      m3_m4_offset:
+			*op++ = LZO_BYTE ((m_off & 63) << 2);
+			*op++ = LZO_BYTE (m_off >> 6);
+		}
+		ii = ip;
+		if (ip >= ip_end)
+			break;
+	}
+
+	*out_len = op - out;
+	return pd (in_end, ii);
+}
+#endif
+
+#ifdef JFFS2_LZO_1
+static int
+lzo1x_1_compress (const lzo_byte * in, lzo_uint in_len,
+		  lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+	lzo_byte *op = out;
+	lzo_uint t;
+
+	if (in_len <= M2_MAX_LEN + 5)
+		t = in_len;
+	else
+	{
+		t = _lzo1x_1_do_compress (in, in_len, op, out_len, wrkmem);
+		op += *out_len;
+	}
+
+	if (t > 0)
+	{
+		const lzo_byte *ii = in + in_len - t;
+
+		if (op == out && t <= 238)
+			*op++ = LZO_BYTE (17 + t);
+		else if (t <= 3)
+			op[-2] |= LZO_BYTE (t);
+		else if (t <= 18)
+			*op++ = LZO_BYTE (t - 3);
+		else
+		{
+			lzo_uint tt = t - 18;
+
+			*op++ = 0;
+			while (tt > 255)
+			{
+				tt -= 255;
+				*op++ = 0;
+			}
+
+			*op++ = LZO_BYTE (tt);
+		}
+		do
+			*op++ = *ii++;
+		while (--t > 0);
+	}
+
+	*op++ = M4_MARKER | 1;
+	*op++ = 0;
+	*op++ = 0;
+
+	*out_len = op - out;
+	return 0;
+}
+#endif
+
+static int
+lzo1x_decompress (const lzo_byte * in, lzo_uint in_len,
+		  lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+	register lzo_byte *op;
+	register const lzo_byte *ip;
+	register lzo_uint t;
+
+	register const lzo_byte *m_pos;
+
+	const lzo_byte *const ip_end = in + in_len;
+	lzo_byte *const op_end = out + *out_len;
+
+	*out_len = 0;
+
+	op = out;
+	ip = in;
+
+	if (*ip > 17)
+	{
+		t = *ip++ - 17;
+		if (t < 4)
+			goto match_next;
+		NEED_OP (t);
+		NEED_IP (t + 1);
+		do
+			*op++ = *ip++;
+		while (--t > 0);
+		goto first_literal_run;
+	}
+
+	while (TEST_IP && TEST_OP)
+	{
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+		if (t == 0)
+		{
+			NEED_IP (1);
+			while (*ip == 0)
+			{
+				t += 255;
+				ip++;
+				NEED_IP (1);
+			}
+			t += 15 + *ip++;
+		}
+		NEED_OP (t + 3);
+		NEED_IP (t + 4);
+		if (PTR_ALIGNED2_4 (op, ip))
+		{
+			COPY4 (op, ip);
+
+			op += 4;
+			ip += 4;
+			if (--t > 0)
+			{
+				if (t >= 4)
+				{
+					do
+					{
+						COPY4 (op, ip);
+						op += 4;
+						ip += 4;
+						t -= 4;
+					}
+					while (t >= 4);
+					if (t > 0)
+						do
+							*op++ = *ip++;
+						while (--t > 0);
+				}
+				else
+					do
+						*op++ = *ip++;
+					while (--t > 0);
+			}
+		}
+		else
+		{
+			*op++ = *ip++;
+			*op++ = *ip++;
+			*op++ = *ip++;
+			do
+				*op++ = *ip++;
+			while (--t > 0);
+		}
+	      first_literal_run:
+
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+
+		m_pos = op - (1 + M2_MAX_OFFSET);
+		m_pos -= t >> 2;
+		m_pos -= *ip++ << 2;
+		TEST_LOOKBEHIND (m_pos, out);
+		NEED_OP (3);
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		*op++ = *m_pos;
+
+		goto match_done;
+
+		while (TEST_IP && TEST_OP)
+		{
+		      match:
+			if (t >= 64)
+			{
+				m_pos = op - 1;
+				m_pos -= (t >> 2) & 7;
+				m_pos -= *ip++ << 3;
+				t = (t >> 5) - 1;
+				TEST_LOOKBEHIND (m_pos, out);
+				NEED_OP (t + 3 - 1);
+				goto copy_match;
+
+			}
+			else if (t >= 32)
+			{
+				t &= 31;
+				if (t == 0)
+				{
+					NEED_IP (1);
+					while (*ip == 0)
+					{
+						t += 255;
+						ip++;
+						NEED_IP (1);
+					}
+					t += 31 + *ip++;
+				}
+
+				m_pos = op - 1;
+				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+
+				ip += 2;
+			}
+			else if (t >= 16)
+			{
+				m_pos = op;
+				m_pos -= (t & 8) << 11;
+
+				t &= 7;
+				if (t == 0)
+				{
+					NEED_IP (1);
+					while (*ip == 0)
+					{
+						t += 255;
+						ip++;
+						NEED_IP (1);
+					}
+					t += 7 + *ip++;
+				}
+
+				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+
+				ip += 2;
+				if (m_pos == op)
+					goto eof_found;
+				m_pos -= 0x4000;
+			}
+			else
+			{
+
+				m_pos = op - 1;
+				m_pos -= t >> 2;
+				m_pos -= *ip++ << 2;
+				TEST_LOOKBEHIND (m_pos, out);
+				NEED_OP (2);
+				*op++ = *m_pos++;
+				*op++ = *m_pos;
+
+				goto match_done;
+			}
+
+			TEST_LOOKBEHIND (m_pos, out);
+			NEED_OP (t + 3 - 1);
+			if (t >= 2 * 4 - (3 - 1)
+			    && PTR_ALIGNED2_4 (op, m_pos))
+			{
+				COPY4 (op, m_pos);
+				op += 4;
+				m_pos += 4;
+				t -= 4 - (3 - 1);
+				do
+				{
+					COPY4 (op, m_pos);
+					op += 4;
+					m_pos += 4;
+					t -= 4;
+				}
+				while (t >= 4);
+				if (t > 0)
+					do
+						*op++ = *m_pos++;
+					while (--t > 0);
+			}
+			else
+
+			{
+			      copy_match:
+				*op++ = *m_pos++;
+				*op++ = *m_pos++;
+				do
+					*op++ = *m_pos++;
+				while (--t > 0);
+			}
+
+		      match_done:
+			t = ip[-2] & 3;
+
+			if (t == 0)
+				break;
+
+		      match_next:
+			NEED_OP (t);
+			NEED_IP (t + 1);
+			do
+				*op++ = *ip++;
+			while (--t > 0);
+			t = *ip++;
+		}
+	}
+	*out_len = op - out;
+	return LZO_E_EOF_NOT_FOUND;
+
+      eof_found:
+	*out_len = op - out;
+	return (ip == ip_end ? LZO_E_OK :
+		(ip <
+		 ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+
+      input_overrun:
+	*out_len = op - out;
+	return LZO_E_INPUT_OVERRUN;
+
+      output_overrun:
+	*out_len = op - out;
+	return LZO_E_OUTPUT_OVERRUN;
+
+      lookbehind_overrun:
+	*out_len = op - out;
+	return LZO_E_LOOKBEHIND_OVERRUN;
+}
+
+/* lzo1x_oo.ch */
+
+#define NO_LIT          LZO_UINT_MAX
+
+static void
+copy2 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
+{
+	ip[0] = m_pos[0];
+	if (off == 1)
+		ip[1] = m_pos[0];
+	else
+		ip[1] = m_pos[1];
+}
+
+static void
+copy3 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
+{
+	ip[0] = m_pos[0];
+	if (off == 1)
+	{
+		ip[2] = ip[1] = m_pos[0];
+	}
+	else if (off == 2)
+	{
+		ip[1] = m_pos[1];
+		ip[2] = m_pos[0];
+	}
+	else
+	{
+		ip[1] = m_pos[1];
+		ip[2] = m_pos[2];
+	}
+}
+
+static int
+lzo1x_optimize (lzo_byte * in, lzo_uint in_len,
+		lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+	register lzo_byte *op;
+	register lzo_byte *ip;
+	register lzo_uint t;
+	register lzo_byte *m_pos;
+	lzo_uint nl;
+	const lzo_byte *const ip_end = in + in_len;
+	const lzo_byte *const op_end = out + *out_len;
+	lzo_byte *litp = NULL;
+	lzo_uint lit = 0;
+	lzo_uint next_lit = NO_LIT;
+	long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
+
+	*out_len = 0;
+
+	op = out;
+	ip = in;
+
+	if (*ip > 17)
+	{
+		t = *ip++ - 17;
+		if (t < 4)
+			goto match_next;
+		goto first_literal_run;
+	}
+
+	while (TEST_IP && TEST_OP)
+	{
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+		litp = ip - 1;
+		if (t == 0)
+		{
+			t = 15;
+			while (*ip == 0)
+				t += 255, ip++;
+			t += *ip++;
+		}
+		lit = t + 3;
+	      copy_literal_run:
+		*op++ = *ip++;
+		*op++ = *ip++;
+		*op++ = *ip++;
+	      first_literal_run:
+		do
+			*op++ = *ip++;
+		while (--t > 0);
+
+		t = *ip++;
+
+		if (t >= 16)
+			goto match;
+		m_pos = op - 1 - 0x800;
+		m_pos -= t >> 2;
+		m_pos -= *ip++ << 2;
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		lit = 0;
+		goto match_done;
+
+		while (TEST_IP && TEST_OP)
+		{
+			if (t < 16)
+			{
+				m_pos = op - 1;
+				m_pos -= t >> 2;
+				m_pos -= *ip++ << 2;
+
+				if (litp == NULL)
+					goto copy_m1;
+
+				nl = ip[-2] & 3;
+				if (nl == 0 && lit == 1 && ip[0] >= 16)
+				{
+					next_lit = nl;
+					lit += 2;
+					*litp = LZO_BYTE ((*litp & ~3) | lit);
+					copy2 (ip - 2, m_pos, op - m_pos);
+					o_m1_a++;
+				}
+				else if (nl == 0 && ip[0] < 16 && ip[0] != 0
+					 && (lit + 2 + ip[0] < 16))
+				{
+					t = *ip++;
+					*litp &= ~3;
+					copy2 (ip - 3 + 1, m_pos, op - m_pos);
+					litp += 2;
+					if (lit > 0)
+						memmove (litp + 1, litp, lit);
+					lit += 2 + t + 3;
+					*litp = LZO_BYTE (lit - 3);
+
+					o_m1_b++;
+					*op++ = *m_pos++;
+					*op++ = *m_pos++;
+					goto copy_literal_run;
+				}
+			      copy_m1:
+				*op++ = *m_pos++;
+				*op++ = *m_pos++;
+			}
+			else
+			{
+			      match:
+				if (t >= 64)
+				{
+					m_pos = op - 1;
+					m_pos -= (t >> 2) & 7;
+					m_pos -= *ip++ << 3;
+					t = (t >> 5) - 1;
+					if (litp == NULL)
+						goto copy_m;
+
+					nl = ip[-2] & 3;
+					if (t == 1 && lit > 3 && nl == 0 &&
+					    ip[0] < 16 && ip[0] != 0
+					    && (lit + 3 + ip[0] < 16))
+					{
+						t = *ip++;
+						copy3 (ip - 1 - 2, m_pos,
+						       op - m_pos);
+						lit += 3 + t + 3;
+						*litp = LZO_BYTE (lit - 3);
+						o_m2++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						goto copy_literal_run;
+					}
+				}
+				else
+				{
+					if (t >= 32)
+					{
+						t &= 31;
+						if (t == 0)
+						{
+							t = 31;
+							while (*ip == 0)
+								t += 255,
+									ip++;
+							t += *ip++;
+						}
+						m_pos = op - 1;
+						m_pos -= *ip++ >> 2;
+						m_pos -= *ip++ << 6;
+					}
+					else
+					{
+						m_pos = op;
+						m_pos -= (t & 8) << 11;
+						t &= 7;
+						if (t == 0)
+						{
+							t = 7;
+							while (*ip == 0)
+								t += 255,
+									ip++;
+							t += *ip++;
+						}
+						m_pos -= *ip++ >> 2;
+						m_pos -= *ip++ << 6;
+						if (m_pos == op)
+							goto eof_found;
+						m_pos -= 0x4000;
+					}
+					if (litp == NULL)
+						goto copy_m;
+
+					nl = ip[-2] & 3;
+					if (t == 1 && lit == 0 && nl == 0
+					    && ip[0] >= 16)
+					{
+						next_lit = nl;
+						lit += 3;
+						*litp = LZO_BYTE ((*litp & ~3)
+								  | lit);
+						copy3 (ip - 3, m_pos,
+						       op - m_pos);
+						o_m3_a++;
+					}
+					else if (t == 1 && lit <= 3 && nl == 0
+						 && ip[0] < 16 && ip[0] != 0
+						 && (lit + 3 + ip[0] < 16))
+					{
+						t = *ip++;
+						*litp &= ~3;
+						copy3 (ip - 4 + 1, m_pos,
+						       op - m_pos);
+						litp += 2;
+						if (lit > 0)
+							memmove (litp + 1,
+								 litp, lit);
+						lit += 3 + t + 3;
+						*litp = LZO_BYTE (lit - 3);
+
+						o_m3_b++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						goto copy_literal_run;
+					}
+				}
+			      copy_m:
+				*op++ = *m_pos++;
+				*op++ = *m_pos++;
+				do
+					*op++ = *m_pos++;
+				while (--t > 0);
+			}
+
+		      match_done:
+			if (next_lit == NO_LIT)
+			{
+				t = ip[-2] & 3;
+				lit = t;
+				litp = ip - 2;
+			}
+			else
+				t = next_lit;
+			next_lit = NO_LIT;
+			if (t == 0)
+				break;
+		      match_next:
+			do
+				*op++ = *ip++;
+			while (--t > 0);
+			t = *ip++;
+		}
+	}
+
+	*out_len = op - out;
+	return LZO_E_EOF_NOT_FOUND;
+
+      eof_found:
+	*out_len = op - out;
+	return (ip == ip_end ? LZO_E_OK :
+		(ip <
+		 ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+}
+
+/* interface to jffs2 follows */
+
+#include "compr.h"
+#include <linux/jffs2.h>
+
+/*#define BLOCKSIZE		JFFS2_PAGE_SIZE
+#define OUTBLOCKSIZE	(BLOCKSIZE + BLOCKSIZE / 64 + 16 + 3)*/
+
+int jffs2_lzo_compress (unsigned char *input,
+			unsigned char *output, uint32_t *sourcelen,
+			uint32_t *dstlen, void *model);
+
+int jffs2_lzo_decompress (unsigned char *input,
+			  unsigned char *output, uint32_t sourcelen,
+			  uint32_t dstlen, void *model);
+
+static struct jffs2_compressor jffs2_lzo_comp = {
+	.priority = JFFS2_LZO_PRIORITY,
+	.name = "lzo",
+	.compr = JFFS2_COMPR_LZO,
+	.compress = &jffs2_lzo_compress,
+	.decompress = &jffs2_lzo_decompress,
+#ifdef JFFS2_LZO_DISABLED
+	.disabled = 1,
+#else
+	.disabled = 0,
+#endif
+};
+
+#ifdef JFFS2_LZO_1
+static int
+no_lzo1x_optimize (lzo_byte * src, lzo_uint src_len,
+		   lzo_byte * dst, lzo_uintp dst_len, lzo_voidp wrkmem)
+{
+	return 0;
+}
+#endif
+
+static lzo_compress_t lzo1x_compressor = lzo1x_999_compress;
+static lzo_optimize_t lzo1x_optimizer = lzo1x_optimize;
+#ifdef JFFS2_LZO_1
+static int lzo1x_compressor_type = 999;
+static int lzo1x_optimize_type = 1;
+#endif
+static unsigned long lzo1x_compressor_memsize = LZO1X_999_MEM_COMPRESS;
+
+static lzo_bytep wrkmem = NULL;	        /* temporary buffer for compression, used by lzo       */
+static lzo_bytep cmprssmem = NULL;	/* temporary buffer for compression, used by interface */
+static int cmprssmem_size = 0;
+
+static int prepare_out_buf(uint32_t ssize, uint32_t dsize)
+{
+        uint32_t msize,req;
+
+        msize = (ssize>dsize)? ssize : dsize;
+        req   = (msize<<1) + 20;
+        if ((!cmprssmem)||(cmprssmem_size<req)) {
+                if (!cmprssmem) {
+                        vfree(cmprssmem);
+                        cmprssmem = NULL;
+                        cmprssmem_size = 0;
+                }
+                cmprssmem = vmalloc(req);
+                if (!cmprssmem) {
+                        return -1;
+                }
+                cmprssmem_size = req;
+        }
+        return 0;
+}
+
+int jffs2_lzo_compress (unsigned char *input,
+			unsigned char *output, uint32_t *sourcelen,
+			uint32_t *dstlen, void *model)
+{
+	lzo_uint csize = *dstlen; /*BLOCKSIZE;*/
+	lzo_uint isize = *sourcelen;
+	int retval;
+
+        if (prepare_out_buf(*sourcelen,*dstlen)) {
+                return -1;
+        }
+	if ((retval =
+	     lzo1x_compressor (input, *sourcelen, cmprssmem, &csize,
+			       wrkmem)) != LZO_E_OK)
+	{
+		return retval;
+	}
+	else
+	{
+		retval = lzo1x_optimizer (cmprssmem, csize, input, &isize,
+					  NULL);
+		if (csize <= *dstlen) {
+			*dstlen = csize;
+			memcpy (output, cmprssmem, csize);
+			return retval;
+		} else {
+			return -1;
+		}
+	}
+}
+
+int jffs2_lzo_decompress (unsigned char *input,
+			  unsigned char *output, uint32_t sourcelen,
+			  uint32_t dstlen, void *model)
+{
+	lzo_uint outlen = dstlen;
+	return lzo1x_decompress (input, sourcelen, output, &outlen, NULL);
+}
+
+int jffs2_lzo_init (void)
+{
+        wrkmem = (lzo_bytep) vmalloc(lzo1x_compressor_memsize);
+        if (!wrkmem) return -1;
+        jffs2_register_compressor(&jffs2_lzo_comp);
+        return 0;
+}
+
+void jffs2_lzo_exit (void)
+{
+	jffs2_unregister_compressor (&jffs2_lzo_comp);
+	if (cmprssmem) vfree(cmprssmem);
+        vfree(wrkmem);
+}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/compr_rtime.c linux-2.4.30/fs/jffs2/compr_rtime.c
--- linux-2.4.30-orig/fs/jffs2/compr_rtime.c	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/fs/jffs2/compr_rtime.c	2006-12-11 18:18:37.000000000 +0100
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: compr_rtime.c,v 1.2 2005/09/28 14:19:41 lel Exp $
  *
  *
  * Very simple lz77-ish encoder.
@@ -25,10 +25,12 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/string.h> 
+#include <linux/jffs2.h> 
+#include "compr.h"
 
 /* _compress returns the compressed size, -1 if bigger */
 int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		   uint32_t *sourcelen, uint32_t *dstlen)
+		   uint32_t *sourcelen, uint32_t *dstlen, void *model)
 {
 	short positions[256];
 	int outpos = 0;
@@ -67,8 +69,8 @@
 }		   
 
 
-void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
-		      uint32_t srclen, uint32_t destlen)
+int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+		      uint32_t srclen, uint32_t destlen, void *model)
 {
 	short positions[256];
 	int outpos = 0;
@@ -98,7 +100,29 @@
 				outpos+=repeat;		
 			}
 		}
-	}		
+	}
+        return 0;
 }		   
 
+static struct jffs2_compressor jffs2_rtime_comp = {
+    .priority = JFFS2_RTIME_PRIORITY,
+    .name = "rtime",
+    .compr = JFFS2_COMPR_RTIME,
+    .compress = &jffs2_rtime_compress,
+    .decompress = &jffs2_rtime_decompress,
+#ifdef JFFS2_RTIME_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_rtime_init(void)
+{
+    return jffs2_register_compressor(&jffs2_rtime_comp);
+}
 
+void jffs2_rtime_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_rtime_comp);
+}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/compr_rubin.c linux-2.4.30/fs/jffs2/compr_rubin.c
--- linux-2.4.30-orig/fs/jffs2/compr_rubin.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/compr_rubin.c	2006-12-11 18:18:37.000000000 +0100
@@ -7,17 +7,17 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $
+ * $Id: compr_rubin.c,v 1.2 2005/09/28 14:19:41 lel Exp $
  *
  */
 
  
 #include <linux/string.h>
 #include <linux/types.h>
+#include <linux/jffs2.h>
 #include "compr_rubin.h"
 #include "histo_mips.h"
-
-
+#include "compr.h"
 
 static void init_rubin(struct rubin_state *rs, int div, int *bits)
 {	
@@ -223,13 +223,13 @@
 #if 0
 /* _compress returns the compressed size, -1 if bigger */
 int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		   uint32_t *sourcelen, uint32_t *dstlen)
+		   uint32_t *sourcelen, uint32_t *dstlen, void *model)
 {
 	return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
 }
 #endif
 int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		   uint32_t *sourcelen, uint32_t *dstlen)
+		   uint32_t *sourcelen, uint32_t *dstlen, void *model)
 {
 	int bits[8];
 	unsigned char histo[256];
@@ -306,14 +306,15 @@
 }		   
 
 
-void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
-		   uint32_t sourcelen, uint32_t dstlen)
+int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t sourcelen, uint32_t dstlen, void *model)
 {
 	rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+        return 0;
 }
 
-void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
-		   uint32_t sourcelen, uint32_t dstlen)
+int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t sourcelen, uint32_t dstlen, void *model)
 {
 	int bits[8];
 	int c;
@@ -322,4 +323,51 @@
 		bits[c] = data_in[c];
 
 	rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
+        return 0;
+}
+
+static struct jffs2_compressor jffs2_rubinmips_comp = {
+    .priority = JFFS2_RUBINMIPS_PRIORITY,
+    .name = "rubinmips",
+    .compr = JFFS2_COMPR_DYNRUBIN,
+    .compress = NULL, /*&jffs2_rubinmips_compress,*/
+    .decompress = &jffs2_rubinmips_decompress,
+#ifdef JFFS2_RUBINMIPS_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_rubinmips_init(void)
+{
+    return jffs2_register_compressor(&jffs2_rubinmips_comp);
+}
+
+void jffs2_rubinmips_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_rubinmips_comp);
+}
+
+static struct jffs2_compressor jffs2_dynrubin_comp = {
+    .priority = JFFS2_DYNRUBIN_PRIORITY,
+    .name = "dynrubin",
+    .compr = JFFS2_COMPR_RUBINMIPS,
+    .compress = jffs2_dynrubin_compress,
+    .decompress = &jffs2_dynrubin_decompress,
+#ifdef JFFS2_DYNRUBIN_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_dynrubin_init(void)
+{
+    return jffs2_register_compressor(&jffs2_dynrubin_comp);
+}
+
+void jffs2_dynrubin_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_dynrubin_comp);
 }
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/compr_rubin.h linux-2.4.30/fs/jffs2/compr_rubin.h
--- linux-2.4.30-orig/fs/jffs2/compr_rubin.h	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/compr_rubin.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,7 +1,7 @@
 /* Rubin encoder/decoder header       */
 /* work started at   : aug   3, 1994  */
 /* last modification : aug  15, 1994  */
-/* $Id: compr_rubin.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ */
+/* $Id: compr_rubin.h,v 1.2 2005/09/28 14:19:41 lel Exp $ */
 
 #include "pushpull.h"
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/compr_zlib.c linux-2.4.30/fs/jffs2/compr_zlib.c
--- linux-2.4.30-orig/fs/jffs2/compr_zlib.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/compr_zlib.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_zlib.c,v 1.25 2003/12/03 09:25:43 dwmw2 Exp $
+ * $Id: compr_zlib.c,v 1.2 2005/09/28 14:19:41 lel Exp $
  *
  */
 
@@ -22,6 +22,7 @@
 #include <linux/zutil.h>
 #include <asm/semaphore.h>
 #include "nodelist.h"
+#include "compr.h"
 
 	/* Plan: call deflate() with avail_in == *sourcelen, 
 		avail_out = *dstlen - 12 and flush == Z_FINISH. 
@@ -40,7 +41,7 @@
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 
-int __init jffs2_zlib_init(void)
+static int __init alloc_workspaces(void)
 {
 	def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
 	if (!def_strm.workspace) {
@@ -58,15 +59,18 @@
 	return 0;
 }
 
-void jffs2_zlib_exit(void)
+static void free_workspaces(void)
 {
 	vfree(def_strm.workspace);
 	vfree(inf_strm.workspace);
 }
+#else
+#define alloc_workspaces() (0)
+#define free_workspaces() do { } while(0)
 #endif /* __KERNEL__ */
 
 int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		   uint32_t *sourcelen, uint32_t *dstlen)
+		   uint32_t *sourcelen, uint32_t *dstlen, void *model)
 {
 	int ret;
 
@@ -131,8 +135,8 @@
 	return ret;
 }
 
-void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
-		      uint32_t srclen, uint32_t destlen)
+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+		      uint32_t srclen, uint32_t destlen, void *model)
 {
 	int ret;
 	int wbits = MAX_WBITS;
@@ -166,7 +170,7 @@
 	if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
 		printk(KERN_WARNING "inflateInit failed\n");
 		up(&inflate_sem);
-		return;
+		return 1;
 	}
 
 	while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
@@ -176,4 +180,39 @@
 	}
 	zlib_inflateEnd(&inf_strm);
 	up(&inflate_sem);
+        return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+    .priority = JFFS2_ZLIB_PRIORITY,
+    .name = "zlib",
+    .compr = JFFS2_COMPR_ZLIB,
+    .compress = &jffs2_zlib_compress,
+    .decompress = &jffs2_zlib_decompress,
+#ifdef JFFS2_ZLIB_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int __init jffs2_zlib_init(void)
+{
+    int ret;
+
+    ret = alloc_workspaces();
+    if (ret)
+        return ret;
+
+    ret = jffs2_register_compressor(&jffs2_zlib_comp);
+    if (ret)
+        free_workspaces();
+
+    return ret;
+}
+
+void jffs2_zlib_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_zlib_comp);
+    free_workspaces();
 }
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/comprtest.c linux-2.4.30/fs/jffs2/comprtest.c
--- linux-2.4.30-orig/fs/jffs2/comprtest.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/comprtest.c	2006-12-11 18:18:37.000000000 +0100
@@ -1,4 +1,4 @@
-/* $Id: comprtest.c,v 1.5 2002/01/03 15:20:44 dwmw2 Exp $ */
+/* $Id: comprtest.c,v 1.2 2005/09/28 14:19:42 lel Exp $ */
 
 #include <linux/kernel.h>
 #include <linux/string.h>
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/crc32.c linux-2.4.30/fs/jffs2/crc32.c
--- linux-2.4.30-orig/fs/jffs2/crc32.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/crc32.c	2006-12-11 18:18:37.000000000 +0100
@@ -37,7 +37,7 @@
  *      polynomial $edb88320
  */
 
-/* $Id: crc32.c,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */
+/* $Id: crc32.c,v 1.2 2005/09/28 14:19:42 lel Exp $ */
 
 #include "crc32.h"
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/crc32.h linux-2.4.30/fs/jffs2/crc32.h
--- linux-2.4.30-orig/fs/jffs2/crc32.h	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/crc32.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,7 +1,7 @@
 #ifndef CRC32_H
 #define CRC32_H
 
-/* $Id: crc32.h,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */
+/* $Id: crc32.h,v 1.2 2005/09/28 14:19:42 lel Exp $ */
 
 #include <linux/types.h>
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/dir.c linux-2.4.30/fs/jffs2/dir.c
--- linux-2.4.30-orig/fs/jffs2/dir.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/dir.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: dir.c,v 1.82 2003/10/11 11:47:23 dwmw2 Exp $
+ * $Id: dir.c,v 1.2 2005/09/28 14:19:42 lel Exp $
  *
  */
 
@@ -217,7 +217,6 @@
 			      dentry->d_name.name, dentry->d_name.len);
 
 	if (ret) {
-		jffs2_clear_inode(inode);
 		make_bad_inode(inode);
 		iput(inode);
 		jffs2_free_raw_inode(ri);
@@ -297,11 +296,11 @@
 	struct jffs2_full_dirent *fd;
 	int namelen;
 	uint32_t alloclen, phys_ofs;
-	int ret;
+	int ret, targetlen = strlen(target);
 
 	/* FIXME: If you care. We'd need to use frags for the target
 	   if it grows much more than this */
-	if (strlen(target) > 254)
+	if (targetlen > 254)
 		return -EINVAL;
 
 	ri = jffs2_alloc_raw_inode();
@@ -315,7 +314,7 @@
 	 * Just the node will do for now, though 
 	 */
 	namelen = dentry->d_name.len;
-	ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
+	ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
 
 	if (ret) {
 		jffs2_free_raw_inode(ri);
@@ -334,16 +333,16 @@
 
 	f = JFFS2_INODE_INFO(inode);
 
-	inode->i_size = strlen(target);
+	inode->i_size = targetlen;
 	ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
 	ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
 	ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
 
 	ri->compr = JFFS2_COMPR_NONE;
-	ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
+	ri->data_crc = cpu_to_je32(crc32(0, target, targetlen));
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	
-	fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
+	fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL);
 
 	jffs2_free_raw_inode(ri);
 
@@ -354,6 +353,20 @@
 		jffs2_clear_inode(inode);
 		return PTR_ERR(fn);
 	}
+
+	/* We use f->dents field to store the target path. */
+	f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
+	if (!f->dents) {
+		printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
+		up(&f->sem);
+		jffs2_complete_reservation(c);
+		jffs2_clear_inode(inode);
+		return -ENOMEM;
+	}
+
+	memcpy(f->dents, target, targetlen + 1);
+	D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
+
 	/* No data here. Only a metadata node, which will be 
 	   obsoleted by the first data write
 	*/
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/erase.c linux-2.4.30/fs/jffs2/erase.c
--- linux-2.4.30-orig/fs/jffs2/erase.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/erase.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: erase.c,v 1.58 2003/11/26 13:02:46 dwmw2 Exp $
+ * $Id: erase.c,v 1.2 2005/09/28 14:19:42 lel Exp $
  *
  */
 
@@ -28,7 +28,7 @@
 #ifndef __ECOS
 static void jffs2_erase_callback(struct erase_info *);
 #endif
-static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
 static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
@@ -36,15 +36,18 @@
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	int ret;
+	uint32_t bad_offset;
 #ifdef __ECOS
        ret = jffs2_flash_erase(c, jeb);
        if (!ret) {
                jffs2_erase_succeeded(c, jeb);
                return;
        }
+       bad_offset = jeb->offset;
 #else /* Linux */
 	struct erase_info *instr;
 
+	D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
 	instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
 	if (!instr) {
 		printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
@@ -65,18 +68,16 @@
 	instr->len = c->sector_size;
 	instr->callback = jffs2_erase_callback;
 	instr->priv = (unsigned long)(&instr[1]);
+	instr->fail_addr = 0xffffffff;
 	
 	((struct erase_priv_struct *)instr->priv)->jeb = jeb;
 	((struct erase_priv_struct *)instr->priv)->c = c;
 
-	/* NAND , read out the fail counter, if possible */
-	if (!jffs2_can_mark_obsolete(c)) 
-		jffs2_nand_read_failcnt(c,jeb);
-		
 	ret = c->mtd->erase(c->mtd, instr);
 	if (!ret)
 		return;
 
+	bad_offset = instr->fail_addr;
 	kfree(instr);
 #endif /* __ECOS */
 
@@ -98,7 +99,7 @@
 	else
 		printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
 
-	jffs2_erase_failed(c, jeb);
+	jffs2_erase_failed(c, jeb, bad_offset);
 }
 
 void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
@@ -166,16 +167,34 @@
 	jffs2_erase_pending_trigger(c);
 }
 
-static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
 {
-	 spin_lock(&c->erase_completion_lock);
-	 c->erasing_size -= c->sector_size;
-	 c->bad_size += c->sector_size;
-	 list_del(&jeb->list);
-	 list_add(&jeb->list, &c->bad_list);
-	 c->nr_erasing_blocks--;
-	 spin_unlock(&c->erase_completion_lock);
-	 wake_up(&c->erase_wait);
+	/* For NAND, if the failure did not occur at the device level for a
+	   specific physical page, don't bother updating the bad block table. */
+	if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
+		/* We had a device-level failure to erase.  Let's see if we've
+		   failed too many times. */
+		if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
+			/* We'd like to give this block another try. */
+			spin_lock(&c->erase_completion_lock);
+			list_del(&jeb->list);
+			list_add(&jeb->list, &c->erase_pending_list);
+			c->erasing_size -= c->sector_size;
+			c->dirty_size += c->sector_size;
+			jeb->dirty_size = c->sector_size;
+			spin_unlock(&c->erase_completion_lock);
+			return;
+		}
+	}
+
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->bad_size += c->sector_size;
+	list_del(&jeb->list);
+	list_add(&jeb->list, &c->bad_list);
+	c->nr_erasing_blocks--;
+	spin_unlock(&c->erase_completion_lock);
+	wake_up(&c->erase_wait);
 }	 
 
 #ifndef __ECOS
@@ -185,7 +204,7 @@
 
 	if(instr->state != MTD_ERASE_DONE) {
 		printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
-		jffs2_erase_failed(priv->c, priv->jeb);
+		jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
 	} else {
 		jffs2_erase_succeeded(priv->c, priv->jeb);
 	}	
@@ -214,7 +233,7 @@
 			continue;
 		} 
 
-		if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
+		if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
 			/* It's in the block we're erasing */
 			struct jffs2_raw_node_ref *this;
 
@@ -258,11 +277,8 @@
 		printk("\n");
 	});
 
-	if (ic->nodes == (void *)ic) {
-		D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
+	if (ic->nodes == (void *)ic)
 		jffs2_del_ino_cache(c, ic);
-		jffs2_free_inode_cache(ic);
-	}
 }
 
 static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
@@ -289,8 +305,9 @@
 	unsigned char *ebuf;
 	size_t retlen;
 	int ret;
+	uint32_t bad_offset;
 
-	if (!jffs2_cleanmarker_oob(c)) {
+	if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0)) {
 		marker_ref = jffs2_alloc_raw_node_ref();
 		if (!marker_ref) {
 			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
@@ -313,6 +330,8 @@
 			uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
 			int i;
 
+			bad_offset = ofs;
+
 			ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
 			if (ret) {
 				printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
@@ -326,22 +345,21 @@
 				/* It's OK. We know it's properly aligned */
 				unsigned long datum = *(unsigned long *)(&ebuf[i]);
 				if (datum + 1) {
-					printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
+					bad_offset += i;
+					printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
 				bad: 
-					if (!jffs2_cleanmarker_oob(c))
+					if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
 						jffs2_free_raw_node_ref(marker_ref);
-					else 
-						jffs2_write_nand_badblock( c ,jeb );
 					kfree(ebuf);
 				bad2:
 					spin_lock(&c->erase_completion_lock);
-					c->erasing_size -= c->sector_size;
-					c->bad_size += c->sector_size;
-
-					list_add_tail(&jeb->list, &c->bad_list);
-					c->nr_erasing_blocks--;
+					/* Stick it on a list (any list) so
+					   erase_failed can take it right off
+					   again.  Silly, but shouldn't happen
+					   often. */
+					list_add(&jeb->list, &c->erasing_list);
 					spin_unlock(&c->erase_completion_lock);
-					wake_up(&c->erase_wait);
+					jffs2_erase_failed(c, jeb, bad_offset);
 					return;
 				}
 			}
@@ -350,7 +368,9 @@
 		}
 		kfree(ebuf);
 	}
-					
+
+	bad_offset = jeb->offset;
+
 	/* Write the erase complete marker */	
 	D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
 	if (jffs2_cleanmarker_oob(c)) {
@@ -364,7 +384,15 @@
 		jeb->used_size = 0;
 		jeb->dirty_size = 0;
 		jeb->wasted_size = 0;
+	} else if (c->cleanmarker_size == 0) {
+		jeb->first_node = jeb->last_node = NULL;
+
+		jeb->free_size = c->sector_size;
+		jeb->used_size = 0;
+		jeb->dirty_size = 0;
+		jeb->wasted_size = 0;
 	} else {
+		struct kvec vecs[1];
 		struct jffs2_unknown_node marker = {
 			.magic =	cpu_to_je16(JFFS2_MAGIC_BITMASK),
 			.nodetype =	cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
@@ -373,15 +401,17 @@
 
 		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
 
-		/* We only write the header; the rest was noise or padding anyway */
-		ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+		vecs[0].iov_base = (unsigned char *) &marker;
+		vecs[0].iov_len = sizeof(marker);
+		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
+		
 		if (ret) {
 			printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
 			       jeb->offset, ret);
 			goto bad2;
 		}
 		if (retlen != sizeof(marker)) {
-			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
+			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
 			       jeb->offset, sizeof(marker), retlen);
 			goto bad2;
 		}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/file.c linux-2.4.30/fs/jffs2/file.c
--- linux-2.4.30-orig/fs/jffs2/file.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/file.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $
+ * $Id: file.c,v 1.2 2005/09/28 14:19:43 lel Exp $
  *
  */
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/fs.c linux-2.4.30/fs/jffs2/fs.c
--- linux-2.4.30-orig/fs/jffs2/fs.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/fs.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: fs.c,v 1.43 2004/05/07 16:00:31 gleixner Exp $
+ * $Id: fs.c,v 1.2 2005/09/28 14:19:43 lel Exp $
  *
  */
 
@@ -20,6 +20,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/pagemap.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <linux/vfs.h>
 #include <linux/crc32.h>
 #include "nodelist.h"
@@ -202,7 +203,7 @@
 
 	buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
 
-	D1(jffs2_dump_block_lists(c));
+	D2(jffs2_dump_block_lists(c));
 
 	spin_unlock(&c->erase_completion_lock);
 
@@ -361,7 +362,7 @@
 	if (!(*flags & MS_RDONLY))
 		jffs2_start_garbage_collect_thread(c);
 	
-	sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
+	*flags |= MS_NOATIME;
 
 	return 0;
 }
@@ -448,23 +449,39 @@
 
 	c = JFFS2_SB_INFO(sb);
 
+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
+	if (c->mtd->type == MTD_NANDFLASH) {
+		printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
+		return -EINVAL;
+	}
+	if (c->mtd->type == MTD_DATAFLASH) {
+		printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
+		return -EINVAL;
+	}
+#endif
+
 	c->flash_size = c->mtd->size;
 
 	/* 
 	 * Check, if we have to concatenate physical blocks to larger virtual blocks
 	 * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
 	 */
-	blocks = c->flash_size / c->mtd->erasesize;
-	while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024))
-		blocks >>= 1;
-	
-	c->sector_size = c->flash_size / blocks;
+	c->sector_size = c->mtd->erasesize; 
+	blocks = c->flash_size / c->sector_size;
+	if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) {
+		while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
+			blocks >>= 1;
+			c->sector_size <<= 1;
+		}	
+	}
+
 	/*
 	 * Size alignment check
 	 */
 	if ((c->sector_size * blocks) != c->flash_size) {
-		printk(KERN_ERR "jffs2: Flash size not aligned to erasesize\n");
-		return -EINVAL;
+		c->flash_size = c->sector_size * blocks;		
+		printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
+			c->flash_size / 1024);
 	}
 
 	if (c->sector_size != c->mtd->erasesize)
@@ -523,7 +540,10 @@
  out_nodes:
 	jffs2_free_ino_caches(c);
 	jffs2_free_raw_node_refs(c);
-	kfree(c->blocks);
+	if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
+		vfree(c->blocks);
+	else
+		kfree(c->blocks);
  out_inohash:
 	kfree(c->inocache_list);
  out_wbuf:
@@ -639,6 +659,19 @@
 	}
 
 	/* add setups for other bizarre flashes here... */
+	if (jffs2_nor_ecc(c)) {
+		ret = jffs2_nor_ecc_flash_setup(c);
+		if (ret)
+			return ret;
+	}
+	
+	/* and Dataflash */
+	if (jffs2_dataflash(c)) {
+		ret = jffs2_dataflash_setup(c);
+		if (ret)
+			return ret;
+	}
+	
 	return ret;
 }
 
@@ -649,4 +682,12 @@
 	}
 
 	/* add cleanups for other bizarre flashes here... */
+	if (jffs2_nor_ecc(c)) {
+		jffs2_nor_ecc_flash_cleanup(c);
+	}
+	
+	/* and DataFlash */
+	if (jffs2_dataflash(c)) {
+		jffs2_dataflash_cleanup(c);
+	}
 }
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/gc.c linux-2.4.30/fs/jffs2/gc.c
--- linux-2.4.30-orig/fs/jffs2/gc.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/gc.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: gc.c,v 1.133 2004/03/08 15:29:09 dwmw2 Exp $
+ * $Id: gc.c,v 1.2 2005/09/28 14:19:43 lel Exp $
  *
  */
 
@@ -19,6 +19,7 @@
 #include <linux/compiler.h>
 #include <linux/stat.h>
 #include "nodelist.h"
+#include "compr.h"
 
 static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
 					  struct jffs2_inode_cache *ic,
@@ -80,7 +81,7 @@
 		nextlist = &c->erasable_list;
 	} else {
 		/* Eep. All were empty */
-		printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
+		D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
 		return NULL;
 	}
 
@@ -102,7 +103,7 @@
 		ret->wasted_size = 0;
 	}
 
-	D1(jffs2_dump_block_lists(c));
+	D2(jffs2_dump_block_lists(c));
 	return ret;
 }
 
@@ -133,7 +134,7 @@
 		if (c->checked_ino > c->highest_ino) {
 			printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
 			       c->unchecked_size);
-			D1(jffs2_dump_block_lists(c));
+			D2(jffs2_dump_block_lists(c));
 			spin_unlock(&c->erase_completion_lock);
 			BUG();
 		}
@@ -204,7 +205,7 @@
 		jeb = jffs2_find_gc_block(c);
 
 	if (!jeb) {
-		printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
+		D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
 		spin_unlock(&c->erase_completion_lock);
 		up(&c->alloc_sem);
 		return -EIO;
@@ -358,10 +359,14 @@
 	spin_unlock(&c->inocache_lock);
 
 	f = jffs2_gc_fetch_inode(c, inum, nlink);
-	if (IS_ERR(f))
-		return PTR_ERR(f);
-	if (!f)
-		return 0;
+	if (IS_ERR(f)) {
+		ret = PTR_ERR(f);
+		goto release_sem;
+	}
+	if (!f) {
+		ret = 0;
+		goto release_sem;
+	}
 
 	ret = jffs2_garbage_collect_live(c, jeb, raw, f);
 
@@ -597,7 +602,7 @@
 			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
                         jffs2_free_raw_node_ref(nraw);
 		}
-		if (!retried && (nraw == jffs2_alloc_raw_node_ref())) {
+		if (!retried && (nraw = jffs2_alloc_raw_node_ref())) {
 			/* Try to reallocate space and retry */
 			uint32_t dummy;
 			struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
@@ -623,6 +628,7 @@
 			jffs2_free_raw_node_ref(nraw);
 		}
 
+		jffs2_free_raw_node_ref(nraw);
 		if (!ret)
 			ret = -EIO;
 		goto out_node;
@@ -632,10 +638,12 @@
 
 	/* Link into per-inode list. This is safe because of the ic
 	   state being INO_STATE_GC. Note that if we're doing this
-	   for an inode which is in-code, the 'nraw' pointer is then
+	   for an inode which is in-core, the 'nraw' pointer is then
 	   going to be fetched from ic->nodes by our caller. */
+	spin_lock(&c->erase_completion_lock);
         nraw->next_in_ino = ic->nodes;
         ic->nodes = nraw;
+	spin_unlock(&c->erase_completion_lock);
 
 	jffs2_mark_node_obsolete(c, raw);
 	D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
@@ -808,8 +816,7 @@
 
 			/* Doesn't matter if there's one in the same erase block. We're going to 
 			   delete it too at the same time. */
-			if ((raw->flash_offset & ~(c->sector_size-1)) ==
-			    (fd->raw->flash_offset & ~(c->sector_size-1)))
+			if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
 				continue;
 
 			D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
@@ -823,7 +830,7 @@
 				continue;
 			}
 			if (retlen != rawlen) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %u) reading header from obsolete node at %08x\n",
 				       retlen, rawlen, ref_offset(raw));
 				continue;
 			}
@@ -1176,7 +1183,7 @@
 	while(offset < orig_end) {
 		uint32_t datalen;
 		uint32_t cdatalen;
-		char comprtype = JFFS2_COMPR_NONE;
+		uint16_t comprtype = JFFS2_COMPR_NONE;
 
 		ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
 
@@ -1209,7 +1216,8 @@
 		ri.offset = cpu_to_je32(offset);
 		ri.csize = cpu_to_je32(cdatalen);
 		ri.dsize = cpu_to_je32(datalen);
-		ri.compr = comprtype;
+		ri.compr = comprtype & 0xff;
+		ri.usercompr = (comprtype >> 8) & 0xff;
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 		ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 	
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/ioctl.c linux-2.4.30/fs/jffs2/ioctl.c
--- linux-2.4.30-orig/fs/jffs2/ioctl.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/ioctl.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $
+ * $Id: ioctl.c,v 1.2 2005/09/28 14:19:43 lel Exp $
  *
  */
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/malloc.c linux-2.4.30/fs/jffs2/malloc.c
--- linux-2.4.30-orig/fs/jffs2/malloc.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/malloc.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $
+ * $Id: malloc.c,v 1.2 2005/09/28 14:19:43 lel Exp $
  *
  */
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/nodelist.c linux-2.4.30/fs/jffs2/nodelist.c
--- linux-2.4.30-orig/fs/jffs2/nodelist.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/nodelist.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $
+ * $Id: nodelist.c,v 1.2 2005/09/28 14:19:44 lel Exp $
  *
  */
 
@@ -92,42 +92,53 @@
 	}
 }
 
+/* Returns first valid node after 'ref'. May return 'ref' */
+static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
+{
+	while (ref && ref->next_in_ino) {
+		if (!ref_obsolete(ref))
+			return ref;
+		D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
+		ref = ref->next_in_ino;
+	}
+	return NULL;
+}
 
 /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
    with this ino, returning the former in order of version */
 
-int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
+int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 			  struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
 			  uint32_t *highest_version, uint32_t *latest_mctime,
 			  uint32_t *mctime_ver)
 {
-	struct jffs2_raw_node_ref *ref = f->inocache->nodes;
+	struct jffs2_raw_node_ref *ref, *valid_ref;
 	struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
 	struct jffs2_full_dirent *fd, *ret_fd = NULL;
-
 	union jffs2_node_union node;
 	size_t retlen;
 	int err;
 
 	*mctime_ver = 0;
-
-	D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino));
-	if (!f->inocache->nodes) {
-		printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino);
-	}
+	
+	D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
 
 	spin_lock(&c->erase_completion_lock);
 
-	for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
-		/* Work out whether it's a data node or a dirent node */
-		if (ref_obsolete(ref)) {
-			/* FIXME: On NAND flash we may need to read these */
-			D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
-			continue;
-		}
+	valid_ref = jffs2_first_valid_node(f->inocache->nodes);
+
+	if (!valid_ref && (f->inocache->ino != 1))
+		printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
+
+	while (valid_ref) {
 		/* We can hold a pointer to a non-obsolete node without the spinlock,
 		   but _obsolete_ nodes may disappear at any time, if the block
-		   they're in gets erased */
+		   they're in gets erased. So if we mark 'ref' obsolete while we're
+		   not holding the lock, it can go away immediately. For that reason,
+		   we find the next valid node first, before processing 'ref'.
+		*/
+		ref = valid_ref;
+		valid_ref = jffs2_first_valid_node(ref->next_in_ino);
 		spin_unlock(&c->erase_completion_lock);
 
 		cond_resched();
@@ -183,7 +194,6 @@
 				err = -ENOMEM;
 				goto free_out;
 			}
-			memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1);
 			fd->raw = ref;
 			fd->version = je32_to_cpu(node.d.version);
 			fd->ino = je32_to_cpu(node.d.ino);
@@ -221,6 +231,7 @@
 			}
 			fd->nhash = full_name_hash(fd->name, node.d.nsize);
 			fd->next = NULL;
+			fd->name[node.d.nsize] = '\0';
 				/* Wheee. We now have a complete jffs2_full_dirent structure, with
 				   the name in it and everything. Link it into the list 
 				*/
@@ -479,6 +490,8 @@
 {
 	struct jffs2_inode_cache **prev;
 	D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
+	BUG_ON(!new->ino);
+
 	spin_lock(&c->inocache_lock);
 	
 	prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
@@ -495,7 +508,7 @@
 void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
 {
 	struct jffs2_inode_cache **prev;
-	D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
+	D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
 	spin_lock(&c->inocache_lock);
 	
 	prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
@@ -507,6 +520,14 @@
 		*prev = old->next;
 	}
 
+	/* Free it now unless it's in READING or CLEARING state, which
+	   are the transitions upon read_inode() and clear_inode(). The
+	   rest of the time we know nobody else is looking at it, and 
+	   if it's held by read_inode() or clear_inode() they'll free it
+	   for themselves. */
+	if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
+		jffs2_free_inode_cache(old);
+
 	spin_unlock(&c->inocache_lock);
 }
 
@@ -519,7 +540,6 @@
 		this = c->inocache_list[i];
 		while (this) {
 			next = this->next;
-			D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
 			jffs2_free_inode_cache(this);
 			this = next;
 		}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/nodelist.h linux-2.4.30/fs/jffs2/nodelist.h
--- linux-2.4.30-orig/fs/jffs2/nodelist.h	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/nodelist.h	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.h,v 1.117 2004/05/05 11:57:52 dwmw2 Exp $
+ * $Id: nodelist.h,v 1.2 2005/09/28 14:19:44 lel Exp $
  *
  */
 
@@ -107,16 +107,6 @@
 #define ref_obsolete(ref)	(((ref)->flash_offset & 3) == REF_OBSOLETE)
 #define mark_ref_normal(ref)    do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
 
-/* 
-   Used for keeping track of deletion nodes &c, which can only be marked
-   as obsolete when the node which they mark as deleted has actually been 
-   removed from the flash.
-*/
-struct jffs2_raw_node_ref_list {
-	struct jffs2_raw_node_ref *rew;
-	struct jffs2_raw_node_ref_list *next;
-};
-
 /* For each inode in the filesystem, we need to keep a record of
    nlink, because it would be a PITA to scan the whole directory tree
    at read_inode() time to calculate it, and to keep sufficient information
@@ -145,16 +135,10 @@
 #define INO_STATE_CHECKEDABSENT	3	/* Checked, cleared again */
 #define INO_STATE_GC		4	/* GCing a 'pristine' node */
 #define INO_STATE_READING	5	/* In read_inode() */
+#define INO_STATE_CLEARING	6	/* In clear_inode() */
 
 #define INOCACHE_HASHSIZE 128
 
-struct jffs2_scan_info {
-	struct jffs2_full_dirent *dents;
-	struct jffs2_tmp_dnode_info *tmpnodes;
-	/* Latest i_size info */
-	uint32_t version;
-	uint32_t isize;
-};
 /*
   Larger representation of a raw node, kept in-core only when the 
   struct inode for this particular ino is instantiated.
@@ -163,12 +147,11 @@
 struct jffs2_full_dnode
 {
 	struct jffs2_raw_node_ref *raw;
-	uint32_t ofs; /* Don't really need this, but optimisation */
+	uint32_t ofs; /* The offset to which the data of this node belongs */
 	uint32_t size;
 	uint32_t frags; /* Number of fragments which currently refer
 			to this node. When this reaches zero, 
-			the node is obsolete.
-		     */
+			the node is obsolete.  */
 };
 
 /* 
@@ -193,6 +176,7 @@
 	unsigned char type;
 	unsigned char name[0];
 };
+
 /*
   Fragments - used to build a map of which raw node to obtain 
   data from for each part of the ino
@@ -202,7 +186,7 @@
 	struct rb_node rb;
 	struct jffs2_full_dnode *node; /* NULL for holes */
 	uint32_t size;
-	uint32_t ofs; /* Don't really need this, but optimisation */
+	uint32_t ofs; /* The offset to which this fragment belongs */
 };
 
 struct jffs2_eraseblock
@@ -221,14 +205,6 @@
 	struct jffs2_raw_node_ref *last_node;
 
 	struct jffs2_raw_node_ref *gc_node;	/* Next node to be garbage collected */
-
-	/* For deletia. When a dirent node in this eraseblock is
-	   deleted by a node elsewhere, that other node can only 
-	   be marked as obsolete when this block is actually erased.
-	   So we keep a list of the nodes to mark as obsolete when
-	   the erase is completed.
-	*/
-	// MAYBE	struct jffs2_raw_node_ref_list *deletia;
 };
 
 #define ACCT_SANITY_CHECK(c, jeb) do { \
@@ -281,9 +257,14 @@
 			else if (!ref_obsolete(ref2)) \
 				my_used_size += ref_totlen(c, jeb, ref2); \
 			if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
-				printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
-				       ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
-				       jeb->last_node, ref_offset(jeb->last_node)); \
+                                if (!ref2->next_phys) \
+				       printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \
+				             ref2, ref_offset(ref2), ref2->next_phys, \
+				             jeb->last_node, ref_offset(jeb->last_node)); \
+                                else \
+                                       printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
+				             ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
+				             jeb->last_node, ref_offset(jeb->last_node)); \
 				paranoia_failed_dump(jeb); \
 				BUG(); \
 			} \
@@ -391,9 +372,9 @@
 #define frag_erase(frag, list) rb_erase(&frag->rb, list);
 
 /* nodelist.c */
-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
+D2(void jffs2_print_frag_list(struct jffs2_inode_info *f));
 void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
-int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
+int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 			  struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
 			  uint32_t *highest_version, uint32_t *latest_mctime,
 			  uint32_t *mctime_ver);
@@ -472,15 +453,6 @@
 			   unsigned char *buf, uint32_t offset, uint32_t len);
 char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
 
-/* compr.c */
-unsigned char jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-			     unsigned char *data_in, unsigned char **cpage_out, 
-			     uint32_t *datalen, uint32_t *cdatalen);
-void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
-int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-		     unsigned char comprtype, unsigned char *cdata_in, 
-		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
-
 /* scan.c */
 int jffs2_scan_medium(struct jffs2_sb_info *c);
 void jffs2_rotate_lists(struct jffs2_sb_info *c);
@@ -492,17 +464,12 @@
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
 
-#ifdef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 /* wbuf.c */
 int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
 int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
 int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 #endif
 
-/* compr_zlib.c */
-int jffs2_zlib_init(void);
-void jffs2_zlib_exit(void);
-
 #endif /* __JFFS2_NODELIST_H__ */
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/nodemgmt.c linux-2.4.30/fs/jffs2/nodemgmt.c
--- linux-2.4.30-orig/fs/jffs2/nodemgmt.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/nodemgmt.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $
+ * $Id: nodemgmt.c,v 1.2 2005/09/28 14:19:44 lel Exp $
  *
  */
 
@@ -308,7 +308,10 @@
 
 	D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
 #if 1
-	if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
+	/* we could get some obsolete nodes after nextblock was refiled
+	   in wbuf.c */
+	if ((c->nextblock || !ref_obsolete(new))
+	    &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) {
 		printk(KERN_WARNING "argh. node added in wrong place\n");
 		jffs2_free_raw_node_ref(new);
 		return -EINVAL;
@@ -332,7 +335,7 @@
 		c->used_size += len;
 	}
 
-	if (!jeb->free_size && !jeb->dirty_size) {
+	if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
 		/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
 		D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
 			  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
@@ -399,6 +402,17 @@
 	}
 	jeb = &c->blocks[blocknr];
 
+	if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
+	    !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
+		/* Hm. This may confuse static lock analysis. If any of the above 
+		   three conditions is false, we're going to return from this 
+		   function without actually obliterating any nodes or freeing
+		   any jffs2_raw_node_refs. So we don't need to stop erases from
+		   happening, or protect against people holding an obsolete
+		   jffs2_raw_node_ref without the erase_completion_lock. */
+		down(&c->erase_free_sem);
+	}
+
 	spin_lock(&c->erase_completion_lock);
 
 	if (ref_flags(ref) == REF_UNCHECKED) {
@@ -456,13 +470,14 @@
 
 	D1(ACCT_PARANOIA_CHECK(jeb));
 
-	if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
-		/* Mount in progress. Don't muck about with the block
+	if (c->flags & JFFS2_SB_FLAG_SCANNING) {
+		/* Flash scanning is in progress. Don't muck about with the block
 		   lists because they're not ready yet, and don't actually
 		   obliterate nodes that look obsolete. If they weren't 
 		   marked obsolete on the flash at the time they _became_
 		   obsolete, there was probably a reason for that. */
 		spin_unlock(&c->erase_completion_lock);
+		/* We didn't lock the erase_free_sem */
 		return;
 	}
 
@@ -515,43 +530,126 @@
 
 	spin_unlock(&c->erase_completion_lock);
 
-	if (!jffs2_can_mark_obsolete(c))
-		return;
-	if (jffs2_is_readonly(c))
+	if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) ||
+		(c->flags & JFFS2_SB_FLAG_BUILDING)) {
+		/* We didn't lock the erase_free_sem */
 		return;
+	}
+
+	/* The erase_free_sem is locked, and has been since before we marked the node obsolete
+	   and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
+	   the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
+	   by jffs2_free_all_node_refs() in erase.c. Which is nice. */
 
 	D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
 	ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
 	if (ret) {
 		printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
-		return;
+		goto out_erase_sem;
 	}
 	if (retlen != sizeof(n)) {
 		printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
-		return;
+		goto out_erase_sem;
 	}
 	if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
 		printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
-		return;
+		goto out_erase_sem;
 	}
 	if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
 		D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
-		return;
+		goto out_erase_sem;
 	}
 	/* XXX FIXME: This is ugly now */
 	n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
 	ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
 	if (ret) {
 		printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
-		return;
+		goto out_erase_sem;
 	}
 	if (retlen != sizeof(n)) {
 		printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
-		return;
+		goto out_erase_sem;
+	}
+
+	/* Nodes which have been marked obsolete no longer need to be
+	   associated with any inode. Remove them from the per-inode list.
+	   
+	   Note we can't do this for NAND at the moment because we need 
+	   obsolete dirent nodes to stay on the lists, because of the
+	   horridness in jffs2_garbage_collect_deletion_dirent(). Also
+	   because we delete the inocache, and on NAND we need that to 
+	   stay around until all the nodes are actually erased, in order
+	   to stop us from giving the same inode number to another newly
+	   created inode. */
+	if (ref->next_in_ino) {
+		struct jffs2_inode_cache *ic;
+		struct jffs2_raw_node_ref **p;
+
+		spin_lock(&c->erase_completion_lock);
+
+		ic = jffs2_raw_ref_to_ic(ref);
+		for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
+			;
+
+		*p = ref->next_in_ino;
+		ref->next_in_ino = NULL;
+
+		if (ic->nodes == (void *)ic)
+			jffs2_del_ino_cache(c, ic);
+
+		spin_unlock(&c->erase_completion_lock);
+	}
+
+
+	/* Merge with the next node in the physical list, if there is one
+	   and if it's also obsolete and if it doesn't belong to any inode */
+	if (ref->next_phys && ref_obsolete(ref->next_phys) &&
+	    !ref->next_phys->next_in_ino) {
+		struct jffs2_raw_node_ref *n = ref->next_phys;
+		
+		spin_lock(&c->erase_completion_lock);
+
+		ref->__totlen += n->__totlen;
+		ref->next_phys = n->next_phys;
+                if (jeb->last_node == n) jeb->last_node = ref;
+		if (jeb->gc_node == n) {
+			/* gc will be happy continuing gc on this node */
+			jeb->gc_node=ref;
+		}
+		spin_unlock(&c->erase_completion_lock);
+
+		jffs2_free_raw_node_ref(n);
+	}
+	
+	/* Also merge with the previous node in the list, if there is one
+	   and that one is obsolete */
+	if (ref != jeb->first_node ) {
+		struct jffs2_raw_node_ref *p = jeb->first_node;
+
+		spin_lock(&c->erase_completion_lock);
+
+		while (p->next_phys != ref)
+			p = p->next_phys;
+		
+		if (ref_obsolete(p) && !ref->next_in_ino) {
+			p->__totlen += ref->__totlen;
+			if (jeb->last_node == ref) {
+				jeb->last_node = p;
+			}
+			if (jeb->gc_node == ref) {
+				/* gc will be happy continuing gc on this node */
+				jeb->gc_node=p;
+			}
+			p->next_phys = ref->next_phys;
+			jffs2_free_raw_node_ref(ref);
+		}
+		spin_unlock(&c->erase_completion_lock);
 	}
+ out_erase_sem:
+	up(&c->erase_free_sem);
 }
 
-#if CONFIG_JFFS2_FS_DEBUG > 0
+#if CONFIG_JFFS2_FS_DEBUG >= 2
 void jffs2_dump_block_lists(struct jffs2_sb_info *c)
 {
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/os-linux.h linux-2.4.30/fs/jffs2/os-linux.h
--- linux-2.4.30-orig/fs/jffs2/os-linux.h	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/os-linux.h	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2002-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: os-linux.h,v 1.42 2004/05/05 12:00:31 dwmw2 Exp $
+ * $Id: os-linux.h,v 1.2 2005/09/28 14:19:44 lel Exp $
  *
  */
 
@@ -23,6 +23,9 @@
 #define kstatfs statfs
 #endif
 
+struct kstatfs;
+struct kvec;
+
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
 #define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
 #define OFNI_EDONI_2SFFJ(f)  (&(f)->vfs_inode)
@@ -94,9 +97,12 @@
 #endif
 }
 
+
 #define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
+#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
 
-#ifndef CONFIG_JFFS2_FS_NAND
+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
+#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
 #define jffs2_can_mark_obsolete(c) (1)
 #define jffs2_cleanmarker_oob(c) (0)
 #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
@@ -105,38 +111,52 @@
 #define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
 #define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
 #define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
-#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
-#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
+#define jffs2_write_nand_badblock(c,jeb,bad_offset) (1)
 #define jffs2_nand_flash_setup(c) (0)
 #define jffs2_nand_flash_cleanup(c) do {} while(0)
 #define jffs2_wbuf_dirty(c) (0)
 #define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
 #define jffs2_wbuf_timeout NULL
 #define jffs2_wbuf_process NULL
+#define jffs2_nor_ecc(c) (0)
+#define jffs2_dataflash(c) (0)
+#define jffs2_nor_ecc_flash_setup(c) (0)
+#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
 
-#else /* NAND support present */
+#else /* NAND and/or ECC'd NOR support present */
 
-#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM)
+#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
+#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
 #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
 
 #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
 #define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
 #define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
-struct kstatfs;
 
 /* wbuf.c */
-int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
 int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
 int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
 int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
 int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
 void jffs2_wbuf_timeout(unsigned long data);
 void jffs2_wbuf_process(void *data);
+int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
+int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
 int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
 void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
-#endif /* NAND */
+
+#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
+int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
+void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
+
+#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
+int jffs2_dataflash_setup(struct jffs2_sb_info *c);
+void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
+
+#endif /* WRITEBUFFER */
 
 /* erase.c */
 static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
@@ -198,16 +218,9 @@
      
 
 /* writev.c */
-int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, 
+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, 
 		       unsigned long count, loff_t to, size_t *retlen);
 
-/* Compression config */
-#define JFFS2_COMPRESSION
-#undef JFFS2_USE_DYNRUBIN /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
-#undef JFFS2_USE_RUBINMIPS /* Disabled 26/2/1. Obsoleted by dynrubin */
-#define JFFS2_USE_ZLIB
-#define JFFS2_USE_RTIME /* rtime does manage to recompress already-compressed data */
-
 
 #endif /* __JFFS2_OS_LINUX_H__ */
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/pushpull.c linux-2.4.30/fs/jffs2/pushpull.c
--- linux-2.4.30-orig/fs/jffs2/pushpull.c	2005-05-12 17:57:37.000000000 +0200
+++ linux-2.4.30/fs/jffs2/pushpull.c	2006-12-11 18:18:37.000000000 +0100
@@ -31,7 +31,7 @@
  * provisions above, a recipient may use your version of this file
  * under either the RHEPL or the GPL.
  *
- * $Id: pushpull.c,v 1.7 2001/09/23 10:04:15 rmk Exp $
+ * $Id: pushpull.c,v 1.2 2005/09/28 14:19:45 lel Exp $
  *
  */
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/pushpull.h linux-2.4.30/fs/jffs2/pushpull.h
--- linux-2.4.30-orig/fs/jffs2/pushpull.h	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/pushpull.h	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001, 2002 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: pushpull.h,v 1.2 2005/09/28 14:19:45 lel Exp $
  *
  */
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/rbtree.c linux-2.4.30/fs/jffs2/rbtree.c
--- linux-2.4.30-orig/fs/jffs2/rbtree.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/rbtree.c	2006-12-11 18:18:37.000000000 +0100
@@ -17,7 +17,7 @@
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-  $Id: rbtree.c,v 1.3 2003/10/16 08:02:19 dwmw2 Exp $
+  $Id: rbtree.c,v 1.2 2005/09/28 14:19:45 lel Exp $
 */
 
 #ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */
@@ -300,6 +300,7 @@
 	if (color == RB_BLACK)
 		__rb_erase_color(child, parent, root);
 }
+#endif /* Before 2.4.11 */
 
 	/* These routines haven't made it into 2.4 (yet) */
 struct rb_node *rb_next(struct rb_node *node)
@@ -338,7 +339,6 @@
 
 	return node->rb_parent;
 }
-#endif /* Before 2.4.11 */
 
 void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root)
 {
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/read.c linux-2.4.30/fs/jffs2/read.c
--- linux-2.4.30-orig/fs/jffs2/read.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/read.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: read.c,v 1.35 2004/03/08 15:29:09 dwmw2 Exp $
+ * $Id: read.c,v 1.2 2005/09/28 14:19:45 lel Exp $
  *
  */
 
@@ -18,6 +18,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/compiler.h>
 #include "nodelist.h"
+#include "compr.h"
 
 int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 		     struct jffs2_full_dnode *fd, unsigned char *buf,
@@ -129,7 +130,7 @@
 	if (ri->compr != JFFS2_COMPR_NONE) {
 		D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
 			  je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); 
-		ret = jffs2_decompress(c, f, ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
+		ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
 		if (ret) {
 			printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
 			goto out_decomprbuf;
@@ -173,7 +174,7 @@
 			if (frag) {
 				D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
 				holesize = min(holesize, frag->ofs - offset);
-				D1(jffs2_print_frag_list(f));
+				D2(jffs2_print_frag_list(f));
 			}
 			D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
 			memset(buf, 0, holesize);
@@ -213,33 +214,3 @@
 	return 0;
 }
 
-/* Core function to read symlink target. */
-char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
-{
-	char *buf;
-	int ret;
-
-	down(&f->sem);
-
-	if (!f->metadata) {
-		printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
-		up(&f->sem);
-		return ERR_PTR(-EINVAL);
-	}
-	buf = kmalloc(f->metadata->size+1, GFP_USER);
-	if (!buf) {
-		up(&f->sem);
-		return ERR_PTR(-ENOMEM);
-	}
-	buf[f->metadata->size]=0;
-
-	ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size);
-
-	up(&f->sem);
-
-	if (ret) {
-		kfree(buf);
-		return ERR_PTR(ret);
-	}
-	return buf;
-}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/readinode.c linux-2.4.30/fs/jffs2/readinode.c
--- linux-2.4.30-orig/fs/jffs2/readinode.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/readinode.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $
+ * $Id: readinode.c,v 1.2 2005/09/28 14:19:45 lel Exp $
  *
  */
 
@@ -22,7 +22,7 @@
 
 static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
 
-#if CONFIG_JFFS2_FS_DEBUG >= 1
+#if CONFIG_JFFS2_FS_DEBUG >= 2
 static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
 {
 	struct jffs2_node_frag *this = frag_first(list);
@@ -56,7 +56,9 @@
 		printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
 	}
 }
+#endif
 
+#if CONFIG_JFFS2_FS_DEBUG >= 1
 static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
 {
 	struct jffs2_node_frag *frag;
@@ -225,7 +227,7 @@
 		   If so, both 'this' and the new node get marked REF_NORMAL so
 		   the GC can take a look.
 		*/
-		if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
+		if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
 			if (this->node)
 				mark_ref_normal(this->node->raw);
 			mark_ref_normal(newfrag->node->raw);
@@ -510,7 +512,7 @@
 	D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
 
 	/* Grab all nodes relevant to this ino */
-	ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
+	ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
 
 	if (ret) {
 		printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
@@ -621,6 +623,40 @@
 		   case. */
 		if (!je32_to_cpu(latest_node->isize))
 			latest_node->isize = latest_node->dsize;
+
+		if (f->inocache->state != INO_STATE_CHECKING) {
+			/* Symlink's inode data is the target path. Read it and
+			 * keep in RAM to facilitate quick follow symlink operation.
+			 * We use f->dents field to store the target path, which
+			 * is somewhat ugly. */
+			f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
+			if (!f->dents) {
+				printk(KERN_WARNING "Can't allocate %d bytes of memory "
+						"for the symlink target path cache\n",
+						je32_to_cpu(latest_node->csize));
+				up(&f->sem);
+				jffs2_do_clear_inode(c, f);
+				return -ENOMEM;
+			}
+			
+			ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
+						je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
+			
+			if (ret  || retlen != je32_to_cpu(latest_node->csize)) {
+				if (retlen != je32_to_cpu(latest_node->csize))
+					ret = -EIO;
+				kfree(f->dents);
+				f->dents = NULL;
+				up(&f->sem);
+				jffs2_do_clear_inode(c, f);
+				return -ret;
+			}
+
+			((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
+			D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
+						(char *)f->dents));
+		}
+		
 		/* fall through... */
 
 	case S_IFBLK:
@@ -670,6 +706,9 @@
 	down(&f->sem);
 	deleted = f->inocache && !f->inocache->nlink;
 
+	if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
+
 	if (f->metadata) {
 		if (deleted)
 			jffs2_mark_node_obsolete(c, f->metadata->raw);
@@ -678,16 +717,27 @@
 
 	jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
 
-	fds = f->dents;
+	/* For symlink inodes we us f->dents to store the target path name */
+	if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
+		if (f->dents) {
+			kfree(f->dents);
+			f->dents = NULL;
+		}
+	} else {
+		fds = f->dents;
 
-	while(fds) {
-		fd = fds;
-		fds = fd->next;
-		jffs2_free_full_dirent(fd);
+		while(fds) {
+			fd = fds;
+			fds = fd->next;
+			jffs2_free_full_dirent(fd);
+		}
 	}
 
-	if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
+	if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
 		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+		if (f->inocache->nodes == (void *)f->inocache)
+			jffs2_del_ino_cache(c, f->inocache);
+	}
 
 	up(&f->sem);
 }
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/scan.c linux-2.4.30/fs/jffs2/scan.c
--- linux-2.4.30-orig/fs/jffs2/scan.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/scan.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: scan.c,v 1.109 2004/03/19 16:40:50 dwmw2 Exp $
+ * $Id: scan.c,v 1.2 2005/09/28 14:19:46 lel Exp $
  *
  */
 #include <linux/kernel.h>
@@ -19,7 +19,7 @@
 #include <linux/compiler.h>
 #include "nodelist.h"
 
-#define EMPTY_SCAN_SIZE 1024
+#define DEFAULT_EMPTY_SCAN_SIZE 1024
 
 #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
 		c->free_size -= _x; c->dirty_size += _x; \
@@ -68,13 +68,21 @@
 static inline int min_free(struct jffs2_sb_info *c)
 {
 	uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
-#ifdef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
 		return c->wbuf_pagesize;
 #endif
 	return min;
 
 }
+
+static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
+	if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
+		return sector_size;
+	else
+		return DEFAULT_EMPTY_SCAN_SIZE;
+}
+
 int jffs2_scan_medium(struct jffs2_sb_info *c)
 {
 	int i, ret;
@@ -104,6 +112,10 @@
 		else
 			buf_size = PAGE_SIZE;
 
+		/* Respect kmalloc limitations */
+		if (buf_size > 128*1024)
+			buf_size = 128*1024;
+
 		D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
 		flashbuf = kmalloc(buf_size, GFP_KERNEL);
 		if (!flashbuf)
@@ -156,11 +168,8 @@
 
 		case BLK_STATE_PARTDIRTY:
                         /* Some data, but not full. Dirty list. */
-                        /* Except that we want to remember the block with most free space,
-                           and stick it in the 'nextblock' position to start writing to it.
-                           Later when we do snapshots, this must be the most recent block,
-                           not the one with most free space.
-                        */
+                        /* We want to remember the block with most free space
+                           and stick it in the 'nextblock' position to start writing to it. */
                         if (jeb->free_size > min_free(c) && 
 			    (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
                                 /* Better candidate for the next writes to go to */
@@ -219,7 +228,7 @@
 		c->dirty_size -= c->nextblock->dirty_size;
 		c->nextblock->dirty_size = 0;
 	}
-#ifdef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
 		/* If we're going to start writing into a block which already 
 		   contains data, and the end of the data isn't page-aligned,
@@ -237,7 +246,7 @@
 	}
 #endif
 	if (c->nr_erasing_blocks) {
-		if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
+		if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { 
 			printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
 			printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
 			ret = -EIO;
@@ -285,7 +294,7 @@
 	uint32_t hdr_crc, buf_ofs, buf_len;
 	int err;
 	int noise = 0;
-#ifdef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	int cleanmarkerfound = 0;
 #endif
 
@@ -294,7 +303,7 @@
 
 	D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
 
-#ifdef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	if (jffs2_cleanmarker_oob(c)) {
 		int ret = jffs2_check_nand_cleanmarker(c, jeb);
 		D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
@@ -315,7 +324,7 @@
 	if (!buf_size) {
 		buf_len = c->sector_size;
 	} else {
-		buf_len = EMPTY_SCAN_SIZE;
+		buf_len = EMPTY_SCAN_SIZE(c->sector_size);
 		err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
 		if (err)
 			return err;
@@ -325,11 +334,11 @@
 	ofs = 0;
 
 	/* Scan only 4KiB of 0xFF before declaring it's empty */
-	while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
+	while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
 		ofs += 4;
 
-	if (ofs == EMPTY_SCAN_SIZE) {
-#ifdef CONFIG_JFFS2_FS_NAND
+	if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 		if (jffs2_cleanmarker_oob(c)) {
 			/* scan oob, take care of cleanmarker */
 			int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
@@ -337,14 +346,15 @@
 			switch (ret) {
 			case 0:		return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
 			case 1: 	return BLK_STATE_ALLDIRTY;
-			case 2: 	return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */
-			case 3:		return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
 			default: 	return ret;
 			}
 		}
 #endif
 		D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
-		return BLK_STATE_ALLFF;	/* OK to erase if all blocks are like this */
+		if (c->cleanmarker_size == 0)
+			return BLK_STATE_CLEANMARKER;	/* don't bother with re-erase */
+		else
+			return BLK_STATE_ALLFF;	/* OK to erase if all blocks are like this */
 	}
 	if (ofs) {
 		D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
@@ -423,8 +433,8 @@
 			/* If we're only checking the beginning of a block with a cleanmarker,
 			   bail now */
 			if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && 
-			    c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) {
-				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE));
+			    c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
+				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
 				return BLK_STATE_CLEANMARKER;
 			}
 
@@ -619,7 +629,7 @@
 	}
 
 	if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size 
-		&& (!jeb->first_node || !jeb->first_node->next_in_ino) )
+		&& (!jeb->first_node || !jeb->first_node->next_phys) )
 		return BLK_STATE_CLEANMARKER;
 		
 	/* move blocks with max 4 byte dirty space to cleanlist */	
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/super-v24.c linux-2.4.30/fs/jffs2/super-v24.c
--- linux-2.4.30-orig/fs/jffs2/super-v24.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/super-v24.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: super-v24.c,v 1.77 2004/05/05 12:00:31 dwmw2 Exp $
+ * $Id: super-v24.c,v 1.2 2005/09/28 14:19:46 lel Exp $
  *
  */
 
@@ -22,6 +22,7 @@
 #include <linux/jffs2.h>
 #include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
+#include "compr.h"
 #include "nodelist.h"
 
 #ifndef MTD_BLOCK_MAJOR
@@ -89,7 +90,10 @@
 	up(&c->alloc_sem);
 	jffs2_free_ino_caches(c);
 	jffs2_free_raw_node_refs(c);
-	kfree(c->blocks);
+	if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
+		vfree(c->blocks);
+	else
+		kfree(c->blocks);
 	jffs2_flash_cleanup(c);
 	kfree(c->inocache_list);
 	if (c->mtd->sync)
@@ -106,7 +110,7 @@
 	int ret;
 
 	printk(KERN_INFO "JFFS2 version 2.2."
-#ifdef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	       " (NAND)"
 #endif
 	       " (C) 2001-2003 Red Hat, Inc.\n");
@@ -125,15 +129,15 @@
 		return -EIO;
 	}
 #endif
-	ret = jffs2_zlib_init();
+	ret = jffs2_compressors_init();
 	if (ret) {
-		printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+		printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
 		goto out;
 	}
 	ret = jffs2_create_slab_caches();
 	if (ret) {
 		printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-		goto out_zlib;
+		goto out_compressors;
 	}
 	ret = register_filesystem(&jffs2_fs_type);
 	if (ret) {
@@ -144,17 +148,16 @@
 
  out_slab:
 	jffs2_destroy_slab_caches();
- out_zlib:
-	jffs2_zlib_exit();
+ out_compressors:
+	jffs2_compressors_exit();
  out:
-
 	return ret;
 }
 
 static void __exit exit_jffs2_fs(void)
 {
 	jffs2_destroy_slab_caches();
-	jffs2_zlib_exit();
+	jffs2_compressors_exit();
 	unregister_filesystem(&jffs2_fs_type);
 }
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/super.c linux-2.4.30/fs/jffs2/super.c
--- linux-2.4.30-orig/fs/jffs2/super.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/super.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: super.c,v 1.93 2004/05/05 12:00:31 dwmw2 Exp $
+ * $Id: super.c,v 1.2 2005/09/28 14:19:46 lel Exp $
  *
  */
 
@@ -24,6 +24,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/ctype.h>
 #include <linux/namei.h>
+#include "compr.h"
 #include "nodelist.h"
 
 static void jffs2_put_super(struct super_block *);
@@ -55,6 +56,16 @@
 	}
 }
 
+static int jffs2_sync_fs(struct super_block *sb, int wait)
+{
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+	down(&c->alloc_sem);
+	jffs2_flush_wbuf_pad(c);
+	up(&c->alloc_sem);	
+	return 0;
+}
+
 static struct super_operations jffs2_super_operations =
 {
 	.alloc_inode =	jffs2_alloc_inode,
@@ -66,6 +77,7 @@
 	.remount_fs =	jffs2_remount_fs,
 	.clear_inode =	jffs2_clear_inode,
 	.dirty_inode =	jffs2_dirty_inode,
+	.sync_fs =	jffs2_sync_fs,
 };
 
 static int jffs2_sb_compare(struct super_block *sb, void *data)
@@ -129,7 +141,7 @@
 		  mtd->index, mtd->name));
 
 	sb->s_op = &jffs2_super_operations;
-	sb->s_flags = flags;
+	sb->s_flags = flags | MS_NOATIME;
 
 	ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
 
@@ -265,7 +277,10 @@
 	up(&c->alloc_sem);
 	jffs2_free_ino_caches(c);
 	jffs2_free_raw_node_refs(c);
-	kfree(c->blocks);
+	if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
+		vfree(c->blocks);
+	else
+		kfree(c->blocks);
 	jffs2_flash_cleanup(c);
 	kfree(c->inocache_list);
 	if (c->mtd->sync)
@@ -294,28 +309,28 @@
 	int ret;
 
 	printk(KERN_INFO "JFFS2 version 2.2."
-#ifdef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	       " (NAND)"
 #endif
 	       " (C) 2001-2003 Red Hat, Inc.\n");
 
 	jffs2_inode_cachep = kmem_cache_create("jffs2_i",
 					     sizeof(struct jffs2_inode_info),
-					     0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
+					     0, SLAB_RECLAIM_ACCOUNT,
 					     jffs2_i_init_once, NULL);
 	if (!jffs2_inode_cachep) {
 		printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
 		return -ENOMEM;
 	}
-	ret = jffs2_zlib_init();
+	ret = jffs2_compressors_init();
 	if (ret) {
-		printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+		printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
 		goto out;
 	}
 	ret = jffs2_create_slab_caches();
 	if (ret) {
 		printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-		goto out_zlib;
+		goto out_compressors;
 	}
 	ret = register_filesystem(&jffs2_fs_type);
 	if (ret) {
@@ -326,9 +341,10 @@
 
  out_slab:
 	jffs2_destroy_slab_caches();
- out_zlib:
-	jffs2_zlib_exit();
+ out_compressors:
+	jffs2_compressors_exit();
  out:
+	kmem_cache_destroy(jffs2_inode_cachep);
 	return ret;
 }
 
@@ -336,7 +352,7 @@
 {
 	unregister_filesystem(&jffs2_fs_type);
 	jffs2_destroy_slab_caches();
-	jffs2_zlib_exit();
+	jffs2_compressors_exit();
 	kmem_cache_destroy(jffs2_inode_cachep);
 }
 
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/symlink-v24.c linux-2.4.30/fs/jffs2/symlink-v24.c
--- linux-2.4.30-orig/fs/jffs2/symlink-v24.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/jffs2/symlink-v24.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,52 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: symlink-v24.c,v 1.2 2005/09/28 14:19:46 lel Exp $
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include "nodelist.h"
+
+int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
+int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+
+struct inode_operations jffs2_symlink_inode_operations =
+{	
+	.readlink =	jffs2_readlink,
+	.follow_link =	jffs2_follow_link,
+	.setattr =	jffs2_setattr
+};
+
+int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+
+	if (!f->dents) {
+		printk(KERN_ERR "jffs2_readlink(): can't find symlink taerget\n");
+		return -EIO;
+	}
+
+	return vfs_readlink(dentry, buffer, buflen, (char *)f->dents);
+}
+
+int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+
+	if (!f->dents) {
+		printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
+		return -EIO;
+	}
+	
+	return vfs_follow_link(nd, (char *)f->dents);
+}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/symlink.c linux-2.4.30/fs/jffs2/symlink.c
--- linux-2.4.30-orig/fs/jffs2/symlink.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/symlink.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001, 2002 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $
+ * $Id: symlink.c,v 1.2 2005/09/28 14:19:47 lel Exp $
  *
  */
 
@@ -15,43 +15,49 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include "nodelist.h"
 
-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
 
 struct inode_operations jffs2_symlink_inode_operations =
 {	
-	.readlink =	jffs2_readlink,
+	.readlink =	generic_readlink,
 	.follow_link =	jffs2_follow_link,
 	.setattr =	jffs2_setattr
 };
 
-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
+static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-	unsigned char *kbuf;
-	int ret;
-
-	kbuf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
-	if (IS_ERR(kbuf))
-		return PTR_ERR(kbuf);
-
-	ret = vfs_readlink(dentry, buffer, buflen, kbuf);
-	kfree(kbuf);
-	return ret;
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+	
+	/*
+	 * We don't acquire the f->sem mutex here since the only data we
+	 * use is f->dents which in case of the symlink inode points to the
+	 * symlink's target path.
+	 *
+	 * 1. If we are here the inode has already built and f->dents has
+	 * to point to the target path.
+	 * 2. Nobody uses f->dents (if the inode is symlink's inode). The
+	 * exception is inode freeing function which frees f->dents. But
+	 * it can't be called while we are here and before VFS has
+	 * stopped using our f->dents string which we provide by means of
+	 * nd_set_link() call.
+	 */
+	
+	if (!f->dents) {
+		printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
+		return -EIO;
+	}
+	D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
+
+	nd_set_link(nd, (char *)f->dents);
+	
+	/*
+	 * We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
+	 * since the only way that may cause f->dents to be changed is iput() operation.
+	 * But VFS will not use f->dents after iput() has been called.
+	 */
+	return 0;
 }
 
-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-	unsigned char *buf;
-	int ret;
-
-	buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
-
-	if (IS_ERR(buf))
-		return PTR_ERR(buf);
-
-	ret = vfs_follow_link(nd, buf);
-	kfree(buf);
-	return ret;
-}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/wbuf.c linux-2.4.30/fs/jffs2/wbuf.c
--- linux-2.4.30-orig/fs/jffs2/wbuf.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/wbuf.c	2006-12-11 18:18:37.000000000 +0100
@@ -2,12 +2,14 @@
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: wbuf.c,v 1.61 2004/04/14 19:01:51 gleixner Exp $
+ * $Id: wbuf.c,v 1.2 2005/09/28 14:19:47 lel Exp $
  *
  */
 
@@ -27,7 +29,7 @@
 #endif
 
 /* max. erase failures before we mark a block bad */
-#define MAX_ERASE_FAILURES 	5
+#define MAX_ERASE_FAILURES 	2
 
 /* two seconds timeout for timed wbuf-flushing */
 #define WBUF_FLUSH_TIMEOUT	2 * HZ
@@ -128,22 +130,11 @@
 	}
 }
 
-/* Recover from failure to write wbuf. Recover the nodes up to the
- * wbuf, not the one which we were starting to try to write. */
+#define REFILE_NOTEMPTY 0
+#define REFILE_ANYWAY   1
 
-static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
+static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
 {
-	struct jffs2_eraseblock *jeb, *new_jeb;
-	struct jffs2_raw_node_ref **first_raw, **raw;
-	size_t retlen;
-	int ret;
-	unsigned char *buf;
-	uint32_t start, end, ofs, len;
-
-	spin_lock(&c->erase_completion_lock);
-
-	jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
-
 	D1(printk("About to refile bad block at %08x\n", jeb->offset));
 
 	D2(jffs2_dump_block_lists(c));
@@ -156,7 +147,7 @@
 		D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
 		list_add(&jeb->list, &c->bad_used_list);
 	} else {
-		BUG();
+		BUG_ON(allow_empty == REFILE_NOTEMPTY);
 		/* It has to have had some nodes or we couldn't be here */
 		D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
 		list_add(&jeb->list, &c->erase_pending_list);
@@ -173,6 +164,25 @@
 
 	ACCT_SANITY_CHECK(c,jeb);
 	D1(ACCT_PARANOIA_CHECK(jeb));
+}
+
+/* Recover from failure to write wbuf. Recover the nodes up to the
+ * wbuf, not the one which we were starting to try to write. */
+
+static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
+{
+	struct jffs2_eraseblock *jeb, *new_jeb;
+	struct jffs2_raw_node_ref **first_raw, **raw;
+	size_t retlen;
+	int ret;
+	unsigned char *buf;
+	uint32_t start, end, ofs, len;
+
+	spin_lock(&c->erase_completion_lock);
+
+	jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
+
+	jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
 
 	/* Find the first node to be recovered, by skipping over every
 	   node which ends before the wbuf starts, or which is obsolete. */
@@ -222,8 +232,12 @@
 		}
 
 		/* Do the read... */
-		ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
-		if (ret == -EIO && retlen == c->wbuf_ofs - start) {
+		if (jffs2_cleanmarker_oob(c))
+			ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
+		else
+			ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
+		
+		if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) {
 			/* ECC recovered */
 			ret = 0;
 		}
@@ -253,17 +267,16 @@
 	ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
 	if (ret) {
 		printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
-		if (buf)
-			kfree(buf);
+		kfree(buf);
 		return;
 	}
 	if (end-start >= c->wbuf_pagesize) {
-		/* Need to do another write immediately. This, btw,
-		 means that we'll be writing from 'buf' and not from
-		 the wbuf. Since if we're writing from the wbuf there
-		 won't be more than a wbuf full of data, now will
-		 there? :) */
-
+		/* Need to do another write immediately, but it's possible
+		   that this is just because the wbuf itself is completely
+		   full, and there's nothing earlier read back from the 
+		   flash. Hence 'buf' isn't necessarily what we're writing 
+		   from. */
+		unsigned char *rewrite_buf = buf?:c->wbuf;
 		uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
 
 		D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
@@ -279,8 +292,11 @@
 			ret = -EIO;
 		} else
 #endif
+		if (jffs2_cleanmarker_oob(c))
 			ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
-						buf, NULL, c->oobinfo);
+						rewrite_buf, NULL, c->oobinfo);
+		else
+			ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
 
 		if (ret || retlen != towrite) {
 			/* Argh. We tried. Really we did. */
@@ -307,10 +323,10 @@
 
 		c->wbuf_len = (end - start) - towrite;
 		c->wbuf_ofs = ofs + towrite;
-		memcpy(c->wbuf, buf + towrite, c->wbuf_len);
+		memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
 		/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
-
-		kfree(buf);
+		if (buf)
+			kfree(buf);
 	} else {
 		/* OK, now we're left with the dregs in whichever buffer we're using */
 		if (buf) {
@@ -390,14 +406,18 @@
    1: Pad, do not adjust nextblock free_size
    2: Pad, adjust nextblock free_size
 */
+#define NOPAD		0
+#define PAD_NOACCOUNT	1
+#define PAD_ACCOUNTING	2
+
 static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
 {
 	int ret;
 	size_t retlen;
 
-	/* Nothing to do if not NAND flash. In particular, we shouldn't
+	/* Nothing to do if not write-buffering the flash. In particular, we shouldn't
 	   del_timer() the timer we never initialised. */
-	if (jffs2_can_mark_obsolete(c))
+	if (!jffs2_is_writebuffered(c))
 		return 0;
 
 	if (!down_trylock(&c->alloc_sem)) {
@@ -406,7 +426,7 @@
 		BUG();
 	}
 
-	if(!c->wbuf || !c->wbuf_len)
+	if (!c->wbuf_len)	/* already checked c->wbuf above */
 		return 0;
 
 	/* claim remaining space on the page
@@ -415,8 +435,12 @@
 	   if we have a switch to next page, we will not have
 	   enough remaining space for this. 
 	*/
-	if (pad) {
+	if (pad && !jffs2_dataflash(c)) {
 		c->wbuf_len = PAD(c->wbuf_len);
+
+		/* Pad with JFFS2_DIRTY_BITMASK initially.  this helps out ECC'd NOR
+		   with 8 byte page size */
+		memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
 		
 		if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
 			struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
@@ -424,9 +448,6 @@
 			padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
 			padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
 			padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
-		} else {
-			/* Pad with JFFS2_DIRTY_BITMASK */
-			memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
 		}
 	}
 	/* else jffs2_flash_writev has actually filled in the rest of the
@@ -442,8 +463,11 @@
 		ret = -EIO;
 	} else 
 #endif
-	ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
-
+	
+	if (jffs2_cleanmarker_oob(c))
+		ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
+	else
+		ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
 
 	if (ret || retlen != c->wbuf_pagesize) {
 		if (ret)
@@ -456,34 +480,37 @@
 
 		jffs2_wbuf_recover(c);
 
-		return ret; 
+		return ret;
 	}
 
-	/* Adjusting free size of next block only, if it's called from fsync ! */
-	if (pad == 2) {
-		D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n"));
-		spin_lock(&c->erase_completion_lock);
-		if (!c->nextblock)
-			BUG();
+	spin_lock(&c->erase_completion_lock);
+
+	/* Adjust free size of the block if we padded. */
+	if (pad && !jffs2_dataflash(c)) {
+		struct jffs2_eraseblock *jeb;
+
+		jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
+
+		D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
+			  (jeb==c->nextblock)?"next":"", jeb->offset));
+
 		/* wbuf_pagesize - wbuf_len is the amount of space that's to be 
 		   padded. If there is less free space in the block than that,
 		   something screwed up */
-		if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
+		if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
 			printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
 			       c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len);
 			printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
-			       c->nextblock->offset, c->nextblock->free_size);
+			       jeb->offset, jeb->free_size);
 			BUG();
 		}
-		c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len);
+		jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len);
 		c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
-		c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
+		jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
 		c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
-		spin_unlock(&c->erase_completion_lock);
 	}
 
 	/* Stick any now-obsoleted blocks on the erase_pending_list */
-	spin_lock(&c->erase_completion_lock);
 	jffs2_refile_wbuf_blocks(c);
 	jffs2_clear_wbuf_ino_list(c);
 	spin_unlock(&c->erase_completion_lock);
@@ -507,6 +534,9 @@
 
 	D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
 
+	if (!c->wbuf)
+		return 0;
+
 	down(&c->alloc_sem);
 	if (!jffs2_wbuf_pending_for_ino(c, ino)) {
 		D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
@@ -520,7 +550,13 @@
 	if (c->unchecked_size) {
 		/* GC won't make any progress for a while */
 		D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
-		ret = __jffs2_flush_wbuf(c, 2);
+		down_write(&c->wbuf_sem);
+		ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
+		/* retry flushing wbuf in case jffs2_wbuf_recover
+		   left some data in the wbuf */
+		if (ret)
+			ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
+		up_write(&c->wbuf_sem);
 	} else while (old_wbuf_len &&
 		      old_wbuf_ofs == c->wbuf_ofs) {
 
@@ -532,7 +568,13 @@
 		if (ret) {
 			/* GC failed. Flush it with padding instead */
 			down(&c->alloc_sem);
-			ret = __jffs2_flush_wbuf(c, 2);
+			down_write(&c->wbuf_sem);
+			ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
+			/* retry flushing wbuf in case jffs2_wbuf_recover
+			   left some data in the wbuf */
+			if (ret)
+				ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
+			up_write(&c->wbuf_sem);
 			break;
 		}
 		down(&c->alloc_sem);
@@ -547,15 +589,32 @@
 /* Pad write-buffer to end and write it, wasting space. */
 int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
 {
-	return __jffs2_flush_wbuf(c, 1);
-}
+	int ret;
+
+	if (!c->wbuf)
+		return 0;
+
+	down_write(&c->wbuf_sem);
+	ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
+	/* retry - maybe wbuf recover left some data in wbuf. */
+	if (ret)
+		ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
+	up_write(&c->wbuf_sem);
 
+	return ret;
+}
 
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
+#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
+#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
+#else
 #define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
 #define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
-int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
+#endif
+
+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
 {
-	struct iovec outvecs[3];
+	struct kvec outvecs[3];
 	uint32_t totlen = 0;
 	uint32_t split_ofs = 0;
 	uint32_t old_totlen;
@@ -567,9 +626,11 @@
 	uint32_t outvec_to = to;
 
 	/* If not NAND flash, don't bother */
-	if (!c->wbuf)
+	if (!jffs2_is_writebuffered(c))
 		return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
 	
+	down_write(&c->wbuf_sem);
+
 	/* If wbuf_ofs is not initialized, set it to target address */
 	if (c->wbuf_ofs == 0xFFFFFFFF) {
 		c->wbuf_ofs = PAGE_DIV(to);
@@ -577,22 +638,33 @@
 		memset(c->wbuf,0xff,c->wbuf_pagesize);
 	}
 
+	/* Fixup the wbuf if we are moving to a new eraseblock.  The checks below
+	   fail for ECC'd NOR because cleanmarker == 16, so a block starts at
+	   xxx0010.  */
+	if (jffs2_nor_ecc(c)) {
+		if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) {
+			c->wbuf_ofs = PAGE_DIV(to);
+			c->wbuf_len = PAGE_MOD(to);
+			memset(c->wbuf,0xff,c->wbuf_pagesize);
+		}
+	}
+	
 	/* Sanity checks on target address. 
 	   It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), 
 	   and it's permitted to write at the beginning of a new 
 	   erase block. Anything else, and you die.
 	   New block starts at xxx000c (0-b = block header)
 	*/
-	if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) {
+	if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
 		/* It's a write to a new block */
 		if (c->wbuf_len) {
 			D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
-			ret = jffs2_flush_wbuf_pad(c);
+			ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
 			if (ret) {
 				/* the underlying layer has to check wbuf_len to do the cleanup */
 				D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
 				*retlen = 0;
-				return ret;
+				goto exit;
 			}
 		}
 		/* set pointer to new block */
@@ -618,7 +690,6 @@
 	invec = 0;
 	outvec = 0;
 
-
 	/* Fill writebuffer first, if already in use */	
 	if (c->wbuf_len) {
 		uint32_t invec_ofs = 0;
@@ -653,14 +724,14 @@
 		}			
 		
 		/* write buffer is full, flush buffer */
-		ret = __jffs2_flush_wbuf(c, 0);
+		ret = __jffs2_flush_wbuf(c, NOPAD);
 		if (ret) {
 			/* the underlying layer has to check wbuf_len to do the cleanup */
 			D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
 			/* Retlen zero to make sure our caller doesn't mark the space dirty.
 			   We've already done everything that's necessary */
 			*retlen = 0;
-			return ret;
+			goto exit;
 		}
 		outvec_to += donelen;
 		c->wbuf_ofs = outvec_to;
@@ -704,19 +775,31 @@
 
 	if (splitvec != -1) {
 		uint32_t remainder;
-		int ret;
 
 		remainder = outvecs[splitvec].iov_len - split_ofs;
 		outvecs[splitvec].iov_len = split_ofs;
 
 		/* We did cross a page boundary, so we write some now */
-		ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); 
+		if (jffs2_cleanmarker_oob(c))
+			ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); 
+		else
+			ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
+		
 		if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
 			/* At this point we have no problem,
-			   c->wbuf is empty. 
+			   c->wbuf is empty. However refile nextblock to avoid
+			   writing again to same address.
 			*/
-			*retlen = donelen;
-			return ret;
+			struct jffs2_eraseblock *jeb;
+
+			spin_lock(&c->erase_completion_lock);
+
+			jeb = &c->blocks[outvec_to / c->sector_size];
+			jffs2_block_refile(c, jeb, REFILE_ANYWAY);
+
+			*retlen = 0;
+			spin_unlock(&c->erase_completion_lock);
+			goto exit;
 		}
 		
 		donelen += wbuf_retlen;
@@ -755,18 +838,22 @@
 	if (c->wbuf_len && ino)
 		jffs2_wbuf_dirties_inode(c, ino);
 
-	return 0;
+	ret = 0;
+	
+exit:
+	up_write(&c->wbuf_sem);
+	return ret;
 }
 
 /*
  *	This is the entry for flash write.
- *	Check, if we work on NAND FLASH, if so build an iovec and write it via vritev
+ *	Check, if we work on NAND FLASH, if so build an kvec and write it via vritev
 */
 int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
 {
-	struct iovec vecs[1];
+	struct kvec vecs[1];
 
-	if (jffs2_can_mark_obsolete(c))
+	if (!jffs2_is_writebuffered(c))
 		return c->mtd->write(c->mtd, ofs, len, retlen, buf);
 
 	vecs[0].iov_base = (unsigned char *) buf;
@@ -782,47 +869,55 @@
 	loff_t	orbf = 0, owbf = 0, lwbf = 0;
 	int	ret;
 
+	if (!jffs2_is_writebuffered(c))
+		return c->mtd->read(c->mtd, ofs, len, retlen, buf);
+
 	/* Read flash */
-	if (!jffs2_can_mark_obsolete(c)) {
+	if (jffs2_cleanmarker_oob(c))
 		ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
+	else
+		ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
 
-		if ( (ret == -EIO) && (*retlen == len) ) {
-			printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
-			       len, ofs);
-			/* 
-			 * We have the raw data without ECC correction in the buffer, maybe 
-			 * we are lucky and all data or parts are correct. We check the node.
-			 * If data are corrupted node check will sort it out.
-			 * We keep this block, it will fail on write or erase and the we
-			 * mark it bad. Or should we do that now? But we should give him a chance.
-			 * Maybe we had a system crash or power loss before the ecc write or  
-			 * a erase was completed.
-			 * So we return success. :)
-			 */
-		 	ret = 0;
-		 }	
-	} else
-		return c->mtd->read(c->mtd, ofs, len, retlen, buf);
+	if ( (ret == -EBADMSG) && (*retlen == len) ) {
+		printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
+		       len, ofs);
+		/* 
+		 * We have the raw data without ECC correction in the buffer, maybe 
+		 * we are lucky and all data or parts are correct. We check the node.
+		 * If data are corrupted node check will sort it out.
+		 * We keep this block, it will fail on write or erase and the we
+		 * mark it bad. Or should we do that now? But we should give him a chance.
+		 * Maybe we had a system crash or power loss before the ecc write or  
+		 * a erase was completed.
+		 * So we return success. :)
+		 */
+	 	ret = 0;
+	}	
 
 	/* if no writebuffer available or write buffer empty, return */
 	if (!c->wbuf_pagesize || !c->wbuf_len)
-		return ret;
+		return ret;;
 
 	/* if we read in a different block, return */
-	if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) 
-		return ret;	
+	if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
+		return ret;
+
+	/* Lock only if we have reason to believe wbuf contains relevant data,
+	   so that checking an erased block during wbuf recovery space allocation
+	   does not deadlock. */
+	down_read(&c->wbuf_sem);
 
 	if (ofs >= c->wbuf_ofs) {
 		owbf = (ofs - c->wbuf_ofs);	/* offset in write buffer */
 		if (owbf > c->wbuf_len)		/* is read beyond write buffer ? */
-			return ret;
+			goto exit;
 		lwbf = c->wbuf_len - owbf;	/* number of bytes to copy */
 		if (lwbf > len)	
 			lwbf = len;
 	} else {	
 		orbf = (c->wbuf_ofs - ofs);	/* offset in read buffer */
 		if (orbf > len)			/* is write beyond write buffer ? */
-			return ret;
+			goto exit;
 		lwbf = len - orbf; 		/* number of bytes to copy */
 		if (lwbf > c->wbuf_len)	
 			lwbf = c->wbuf_len;
@@ -830,6 +925,8 @@
 	if (lwbf > 0)
 		memcpy(buf+orbf,c->wbuf+owbf,lwbf);
 
+exit:
+	up_read(&c->wbuf_sem);
 	return ret;
 }
 
@@ -844,9 +941,8 @@
 	size_t  retlen;
 	int	oob_size;
 
-	oob_size = c->mtd->oobsize;
-
 	/* allocate a buffer for all oob data in this sector */
+	oob_size = c->mtd->oobsize;
 	len = 4 * oob_size;
 	buf = kmalloc(len, GFP_KERNEL);
 	if (!buf) {
@@ -870,35 +966,23 @@
 		goto out;
 	}
 	
-	/* Special check for first two pages */
-	for (page = 0; page < 2 * oob_size; page += oob_size) {
-		/* Check for bad block marker */
-		if (buf[page+c->badblock_pos] != 0xff) {
-			D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset));
-			/* Return 2 for bad and 3 for failed block 
-			   bad goes to list_bad and failed to list_erase */
-			ret = (!page) ? 2 : 3;
+	/* Special check for first page */
+	for(i = 0; i < oob_size ; i++) {
+		/* Yeah, we know about the cleanmarker. */
+		if (mode && i >= c->fsdata_pos && 
+		    i < c->fsdata_pos + c->fsdata_len)
+			continue;
+
+		if (buf[i] != 0xFF) {
+			D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
+				  buf[page+i], page+i, jeb->offset));
+			ret = 1; 
 			goto out;
 		}
-		for(i = 0; i < oob_size ; i++) {
-			/* Yeah, we know about the cleanmarker. */
-			if (mode && i >= c->fsdata_pos && 
-			    i < c->fsdata_pos+c->fsdata_len)
-				continue;
-
-			if (buf[page+i] != 0xFF) {
-				D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
-					  buf[page+i], page+i, jeb->offset));
-				ret = 1; 
-				goto out;
-			}
-		}
-		/* only the first page can contain a cleanmarker !*/
-		mode = 0;
-	}	
+	}
 
 	/* we know, we are aligned :) */	
-	for (; page < len; page += sizeof(long)) {
+	for (page = oob_size; page < len; page += sizeof(long)) {
 		unsigned long dat = *(unsigned long *)(&buf[page]);
 		if(dat != -1) {
 			ret = 1; 
@@ -932,6 +1016,11 @@
 
 	/* Loop through the physical blocks */
 	for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
+		/* Check first if the block is bad. */
+		if (c->mtd->block_isbad (c->mtd, offset)) {
+			D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
+			return 2;
+		}
 		/*
 		   *    We read oob data from page 0 and 1 of the block.
 		   *    page 0 contains cleanmarker and badblock info
@@ -948,19 +1037,6 @@
 			return -EIO;
 		}
 
-		/* Check for bad block marker */
-		if (buf[c->badblock_pos] != 0xff) {
-			D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n",
-				    jeb->offset, buf[c->badblock_pos],  buf[c->badblock_pos + oob_size], c->badblock_pos));
-			return 2;
-		}
-
-		/* Check for failure counter in the second page */
-		if (buf[c->badblock_pos + oob_size] != 0xff) {
-			D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size]));
-			return 3;
-		}
-
 		/* Check cleanmarker only on the first physical block */
 		if (!cnt) {
 			n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
@@ -1011,98 +1087,35 @@
 }
 
 /* 
- * We try to get the failure count of this block.
- */
-int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
-
-	unsigned char buf[NAND_MAX_OOBSIZE];
-	int	ret;
-	size_t 	retlen;
-	int	oob_size;
-
-	oob_size = c->mtd->oobsize;
-	
-	ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf);
-	
-	if (ret) {
-		D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
-		return ret;
-	}
-
-	if (retlen < oob_size) {
-		D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset));
-		return -EIO;
-	}
-
-	jeb->bad_count =  buf[c->badblock_pos];	
-	return 0;
-}
-
-/* 
- * On NAND we try to mark this block bad. We try to write how often
- * the block was erased and mark it finaly bad, if the count
- * is > MAX_ERASE_FAILURES. We read this information on mount !
- * jeb->bad_count contains the count before this erase.
+ * On NAND we try to mark this block bad. If the block was erased more
+ * than MAX_ERASE_FAILURES we mark it finaly bad.
  * Don't care about failures. This block remains on the erase-pending
  * or badblock list as long as nobody manipulates the flash with
  * a bootloader or something like that.
  */
 
-int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
 {
-	unsigned char buf = 0x0;
 	int 	ret;
-	size_t 	retlen;
 
 	/* if the count is < max, we try to write the counter to the 2nd page oob area */
-	if( ++jeb->bad_count < MAX_ERASE_FAILURES) {
-		buf = (unsigned char)jeb->bad_count;
-		c->badblock_pos += c->mtd->oobblock;
-	}
-	
-	ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf);
+	if( ++jeb->bad_count < MAX_ERASE_FAILURES)
+		return 0;
+
+	if (!c->mtd->block_markbad)
+		return 1; // What else can we do?
+
+	D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
+	ret = c->mtd->block_markbad(c->mtd, bad_offset);
 	
 	if (ret) {
 		D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
 		return ret;
 	}
-	if (retlen != 1) {
-		D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen));
-		return ret;
-	}
-	return 0;
+	return 1;
 }
 
-#define JFFS2_OOB_ECCPOS0		0
-#define JFFS2_OOB_ECCPOS1		1
-#define JFFS2_OOB_ECCPOS2		2
-#define JFFS2_OOB_ECCPOS3		3
-#define JFFS2_OOB_ECCPOS4		6
-#define JFFS2_OOB_ECCPOS5		7
-
-#define NAND_JFFS2_OOB8_FSDAPOS		6
-#define NAND_JFFS2_OOB16_FSDAPOS	8
-#define NAND_JFFS2_OOB64_FSDAPOS	16
-#define NAND_JFFS2_OOB8_FSDALEN		2
 #define NAND_JFFS2_OOB16_FSDALEN	8
-#define NAND_JFFS2_OOB64_FSDALEN	8
-
-static struct nand_oobinfo jffs2_oobinfo_swecc_8 = {
-	.useecc = MTD_NANDECC_PLACE,
-	.eccbytes = 3,
-	.eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2}
-};
-
-static struct nand_oobinfo jffs2_oobinfo_swecc_16 = {
-	.useecc = MTD_NANDECC_PLACE,
-	.eccbytes = 6,
-	.eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2,
-		   JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5}
-};
-
-static struct nand_oobinfo jffs2_oobinfo_autoplace = {
-	.useecc = MTD_NANDECC_AUTOPLACE,
-};
 
 static struct nand_oobinfo jffs2_oobinfo_docecc = {
 	.useecc = MTD_NANDECC_PLACE,
@@ -1111,61 +1124,63 @@
 };
 
 
-
-int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
+int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
 {
+	struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
+
+	/* Do this only, if we have an oob buffer */
+	if (!c->mtd->oobsize)
+		return 0;
+	
 	/* Cleanmarker is out-of-band, so inline size zero */
 	c->cleanmarker_size = 0;
 
-	/* Initialise write buffer */
-	c->wbuf_pagesize = c->mtd->oobblock;
-	c->wbuf_ofs = 0xFFFFFFFF;
-
-	/* FIXME: If we had a generic way of describing the hardware's
-	   use of OOB area, we could perhaps make this generic too. */
-	switch(c->mtd->ecctype) {
-	case MTD_ECC_SW:
-		D1(printk(KERN_DEBUG "JFFS2 using software ECC\n"));
-		switch (c->mtd->oobsize) {
-		case 8:
-			c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS;
-			c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN;
-			c->oobinfo = &jffs2_oobinfo_swecc_8;
-			c->badblock_pos = 5;
-			break;
-		case 16:
-			c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS;
+	/* Should we use autoplacement ? */
+	if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
+		D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
+		/* Get the position of the free bytes */
+		if (!oinfo->oobfree[0][1]) {
+			printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
+			return -ENOSPC;
+		}
+		c->fsdata_pos = oinfo->oobfree[0][0];
+		c->fsdata_len = oinfo->oobfree[0][1];
+		if (c->fsdata_len > 8)
+			c->fsdata_len = 8;
+	} else {
+		/* This is just a legacy fallback and should go away soon */
+		switch(c->mtd->ecctype) {
+		case MTD_ECC_RS_DiskOnChip:
+			printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
+			c->oobinfo = &jffs2_oobinfo_docecc;
+			c->fsdata_pos = 6;
 			c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
-			c->oobinfo = &jffs2_oobinfo_swecc_16;
-			c->badblock_pos = 5;
+			c->badblock_pos = 15;
 			break;
-		case 64:
-			c->fsdata_pos = NAND_JFFS2_OOB64_FSDAPOS;
-			c->fsdata_len = NAND_JFFS2_OOB64_FSDALEN;
-			c->oobinfo = &jffs2_oobinfo_autoplace;
-			c->badblock_pos = 0;
-			break;
-		}	
-		break;
-
-	case MTD_ECC_RS_DiskOnChip:
-		D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n"));
-		c->oobinfo = &jffs2_oobinfo_docecc;
-		c->fsdata_pos = 6;
-		c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
-		c->badblock_pos = 15;
-		break;
-
-	default:
-		printk("JFFS2 doesn't yet know how to handle ECC type %d\n",
-		       c->mtd->ecctype);
-		return -EINVAL;
+	
+		default:
+			D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
+			return -EINVAL;
+		}
 	}
+	return 0;
+}
 
+int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
+{
+	int res;
+
+	/* Initialise write buffer */
+	init_rwsem(&c->wbuf_sem);
+	c->wbuf_pagesize = c->mtd->oobblock;
+	c->wbuf_ofs = 0xFFFFFFFF;
+	
 	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
 	if (!c->wbuf)
 		return -ENOMEM;
 
+	res = jffs2_nand_set_oobinfo(c);
+
 #ifdef BREAKME
 	if (!brokenbuf)
 		brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
@@ -1175,10 +1190,51 @@
 	}
 	memset(brokenbuf, 0xdb, c->wbuf_pagesize);
 #endif
-	return 0;
+	return res;
 }
 
 void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
 {
 	kfree(c->wbuf);
 }
+
+int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
+	c->cleanmarker_size = 0;		/* No cleanmarkers needed */
+	
+	/* Initialize write buffer */
+	init_rwsem(&c->wbuf_sem);
+	c->wbuf_pagesize = c->sector_size;
+	c->wbuf_ofs = 0xFFFFFFFF;
+
+	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+	if (!c->wbuf)
+		return -ENOMEM;
+
+	printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize);
+
+	return 0;
+}
+
+void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
+	kfree(c->wbuf);
+}
+
+int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
+	/* Cleanmarker is actually larger on the flashes */
+	c->cleanmarker_size = 16;
+
+	/* Initialize write buffer */
+	init_rwsem(&c->wbuf_sem);
+	c->wbuf_pagesize = c->mtd->eccsize;
+	c->wbuf_ofs = 0xFFFFFFFF;
+
+	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+	if (!c->wbuf)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
+	kfree(c->wbuf);
+}
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/write.c linux-2.4.30/fs/jffs2/write.c
--- linux-2.4.30-orig/fs/jffs2/write.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/write.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: write.c,v 1.83 2004/03/30 09:36:09 dwmw2 Exp $
+ * $Id: write.c,v 1.2 2005/09/28 14:19:47 lel Exp $
  *
  */
 
@@ -18,6 +18,7 @@
 #include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
 #include "nodelist.h"
+#include "compr.h"
 
 
 int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
@@ -91,7 +92,7 @@
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dnode *fn;
 	size_t retlen;
-	struct iovec vecs[2];
+	struct kvec vecs[2];
 	int ret;
 	int retried = 0;
 	unsigned long cnt = 2;
@@ -135,6 +136,15 @@
 	raw->__totlen = PAD(sizeof(*ri)+datalen);
 	raw->next_phys = NULL;
 
+	if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
+		BUG_ON(!retried);
+		D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
+				"highest version %d -> updating dnode\n", 
+				je32_to_cpu(ri->version), f->highest_version));
+		ri->version = cpu_to_je32(++f->highest_version);
+		ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+	}
+
 	ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
 				 (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
 
@@ -212,8 +222,10 @@
 	jffs2_add_physical_node_ref(c, raw);
 
 	/* Link into per-inode list */
+	spin_lock(&c->erase_completion_lock);
 	raw->next_in_ino = f->inocache->nodes;
 	f->inocache->nodes = raw;
+	spin_unlock(&c->erase_completion_lock);
 
 	D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
 		  flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), 
@@ -232,7 +244,7 @@
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dirent *fd;
 	size_t retlen;
-	struct iovec vecs[2];
+	struct kvec vecs[2];
 	int retried = 0;
 	int ret;
 
@@ -277,6 +289,16 @@
 	raw->__totlen = PAD(sizeof(*rd)+namelen);
 	raw->next_phys = NULL;
 
+	if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
+		BUG_ON(!retried);
+		D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
+				     "highest version %d -> updating dirent\n",
+				     je32_to_cpu(rd->version), f->highest_version));
+		rd->version = cpu_to_je32(++f->highest_version);
+		fd->version = je32_to_cpu(rd->version);
+		rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	}
+
 	ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
 				 (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
 	if (ret || (retlen != sizeof(*rd) + namelen)) {
@@ -332,8 +354,10 @@
 	raw->flash_offset |= REF_PRISTINE;
 	jffs2_add_physical_node_ref(c, raw);
 
+	spin_lock(&c->erase_completion_lock);
 	raw->next_in_ino = f->inocache->nodes;
 	f->inocache->nodes = raw;
+	spin_unlock(&c->erase_completion_lock);
 
 	if (retried) {
 		ACCT_SANITY_CHECK(c,NULL);
@@ -358,7 +382,7 @@
 	while(writelen) {
 		struct jffs2_full_dnode *fn;
 		unsigned char *comprbuf = NULL;
-		unsigned char comprtype = JFFS2_COMPR_NONE;
+		uint16_t comprtype = JFFS2_COMPR_NONE;
 		uint32_t phys_ofs, alloclen;
 		uint32_t datalen, cdatalen;
 		int retried = 0;
@@ -388,7 +412,8 @@
 		ri->offset = cpu_to_je32(offset);
 		ri->csize = cpu_to_je32(cdatalen);
 		ri->dsize = cpu_to_je32(datalen);
-		ri->compr = comprtype;
+		ri->compr = comprtype & 0xff;
+		ri->usercompr = (comprtype >> 8 ) & 0xff;
 		ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 		ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 
@@ -619,20 +644,23 @@
 
 		down(&dead_f->sem);
 
-		while (dead_f->dents) {
-			/* There can be only deleted ones */
-			fd = dead_f->dents;
-			
-			dead_f->dents = fd->next;
-			
-			if (fd->ino) {
-				printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
-				       dead_f->inocache->ino, fd->name, fd->ino);
-			} else {
-				D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
+		if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
+			while (dead_f->dents) {
+				/* There can be only deleted ones */
+				fd = dead_f->dents;
+				
+				dead_f->dents = fd->next;
+				
+				if (fd->ino) {
+					printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
+					       dead_f->inocache->ino, fd->name, fd->ino);
+				} else {
+					D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n",
+						fd->name, dead_f->inocache->ino));
+				}
+				jffs2_mark_node_obsolete(c, fd->raw);
+				jffs2_free_full_dirent(fd);
 			}
-			jffs2_mark_node_obsolete(c, fd->raw);
-			jffs2_free_full_dirent(fd);
 		}
 
 		dead_f->inocache->nlink--;
diff -x asm -Naur linux-2.4.30-orig/fs/jffs2/writev.c linux-2.4.30/fs/jffs2/writev.c
--- linux-2.4.30-orig/fs/jffs2/writev.c	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/fs/jffs2/writev.c	2006-12-11 18:18:37.000000000 +0100
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001, 2002 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $
+ * $Id: writev.c,v 1.2 2005/09/28 14:19:47 lel Exp $
  *
  */
 
@@ -18,7 +18,7 @@
 /* This ought to be in core MTD code. All registered MTD devices
    without writev should have this put in place. Bug the MTD
    maintainer */
-static inline int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs,
+static inline int mtd_fake_writev(struct mtd_info *mtd, const struct kvec *vecs,
 				  unsigned long count, loff_t to, size_t *retlen)
 {
 	unsigned long i;
@@ -39,7 +39,7 @@
 	return ret;
 }
 
-int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
 			      unsigned long count, loff_t to, size_t *retlen)
 {
 	if (c->mtd->writev)
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/Makefile linux-2.4.30/fs/yaffs/Makefile
--- linux-2.4.30-orig/fs/yaffs/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/Makefile	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,14 @@
+
+#
+# Makefile for the linux YAFFS filesystem routines.
+#
+
+O_TARGET := yaffs.o
+
+obj-$(CONFIG_YAFFS_FS) := yaffs_ecc.o yaffs_fs.o yaffs_guts.o
+
+obj-$(CONFIG_YAFFS_MTD_ENABLED) += yaffs_mtdif.o
+obj-$(CONFIG_YAFFS_RAM_ENABLED) += yaffs_ramem.o
+
+include $(TOPDIR)/Rules.make
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/devextras.h linux-2.4.30/fs/yaffs/devextras.h
--- linux-2.4.30-orig/fs/yaffs/devextras.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/devextras.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,271 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * devextras.h
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * This file is just holds extra declarations used during development.
+ * Most of these are from kernel includes placed here so we can use them in 
+ * applications.
+ *
+ * $Id: devextras.h,v 1.1 2005/10/13 15:18:46 lel Exp $
+ *
+ */
+ 
+#ifndef __EXTRAS_H__
+#define __EXTRAS_H__
+
+#if defined WIN32
+#define __inline__ __inline
+#define new newHack
+#endif
+
+#if !(defined __KERNEL__) || (defined WIN32)
+
+// User space defines
+
+typedef unsigned char   __u8;
+typedef unsigned short  __u16;
+typedef unsigned        __u32;
+
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+ 
+ #define prefetch(x) 1
+ 
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+	struct list_head * prev,
+	struct list_head * next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+				  struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+	struct list_head *first = list->next;
+
+	if (first != list) {
+		struct list_head *last = list->prev;
+		struct list_head *at = head->next;
+
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, prefetch(pos->next))
+
+/**
+ * list_for_each_safe	-	iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+
+
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN	0
+#define DT_FIFO		1
+#define DT_CHR		2
+#define DT_DIR		4
+#define DT_BLK		6
+#define DT_REG		8
+#define DT_LNK		10
+#define DT_SOCK		12
+#define DT_WHT		14
+
+#ifndef WIN32
+#include <sys/stat.h>
+#endif
+
+/*
+ * Attribute flags.  These should be or-ed together to figure out what
+ * has been changed!
+ */
+#define ATTR_MODE	1
+#define ATTR_UID	2
+#define ATTR_GID	4
+#define ATTR_SIZE	8
+#define ATTR_ATIME	16
+#define ATTR_MTIME	32
+#define ATTR_CTIME	64
+#define ATTR_ATIME_SET	128
+#define ATTR_MTIME_SET	256
+#define ATTR_FORCE	512	/* Not a change, but a change it */
+#define ATTR_ATTR_FLAG	1024
+
+
+struct iattr {
+	unsigned int	ia_valid;
+	unsigned		ia_mode;
+	unsigned		ia_uid;
+	unsigned		ia_gid;
+	unsigned		ia_size;
+	unsigned		ia_atime;
+	unsigned	ia_mtime;
+	unsigned	ia_ctime;
+	unsigned int	ia_attr_flags;
+};
+
+#define KERN_DEBUG
+
+
+#else
+
+#ifndef WIN32
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#endif
+
+#endif
+
+
+
+#if defined WIN32
+#undef new
+#endif 
+
+#endif
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_ecc.c linux-2.4.30/fs/yaffs/yaffs_ecc.c
--- linux-2.4.30-orig/fs/yaffs/yaffs_ecc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_ecc.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,194 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * yaffs_ecc.c: ECC generation/correction algorithms.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ */
+ 
+ /*
+ * This code implements the ECC algorithm used in SmartMedia.
+ *
+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. 
+ * The two unused bit are set to 1.
+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC 
+ * blocks are used on a 512-byte NAND page.
+ *
+ */
+
+// Table generated by gen-ecc.c
+// Using a table means we do not have to calculate p1..p4 and p1'..p4'
+// for each byte of data. These are instead provided in a table in bits7..2.
+// Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore
+// this bytes influence on the line parity.
+
+const char *yaffs_ecc_c_version = "$Id: yaffs_ecc.c,v 1.1 2005/10/13 15:18:46 lel Exp $";
+
+
+#include "yaffs_ecc.h"
+
+static const unsigned char column_parity_table[] = {
+0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, 
+0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, 
+0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, 
+0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, 
+0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, 
+0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, 
+0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, 
+0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, 
+0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, 
+0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, 
+0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, 
+0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, 
+0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, 
+0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, 
+0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, 
+0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, 
+};
+
+
+static int yaffs_CountBits(unsigned char x)
+{
+	int r = 0;
+	while(x)
+	{
+		if(x & 1) r++;
+		x >>= 1;
+	}
+	return r;
+}
+
+void yaffs_ECCCalculate(const unsigned char *data,unsigned char *ecc)
+{
+	unsigned int i;
+	
+	unsigned char col_parity = 0;
+	unsigned char line_parity = 0;
+	unsigned char line_parity_prime = 0;
+	unsigned char t;
+	unsigned char b;
+	
+	for(i = 0; i < 256; i++)
+	{
+		b = column_parity_table[*data++];
+		col_parity ^= b;
+
+		if(b & 0x01) // odd number of bits in the byte
+		{
+			line_parity ^= i;
+			line_parity_prime ^= ~i;
+		}
+		
+	}
+	
+	ecc[2] = (~col_parity) | 0x03;
+	
+	t = 0;
+	if(line_parity       & 0x80) t |= 0x80;
+	if(line_parity_prime & 0x80) t |= 0x40;
+	if(line_parity       & 0x40) t |= 0x20;
+	if(line_parity_prime & 0x40) t |= 0x10;
+	if(line_parity       & 0x20) t |= 0x08;
+	if(line_parity_prime & 0x20) t |= 0x04;
+	if(line_parity       & 0x10) t |= 0x02;
+	if(line_parity_prime & 0x10) t |= 0x01;
+	ecc[1] = ~t;
+	
+	t = 0;
+	if(line_parity       & 0x08) t |= 0x80;
+	if(line_parity_prime & 0x08) t |= 0x40;
+	if(line_parity       & 0x04) t |= 0x20;
+	if(line_parity_prime & 0x04) t |= 0x10;
+	if(line_parity       & 0x02) t |= 0x08;
+	if(line_parity_prime & 0x02) t |= 0x04;
+	if(line_parity       & 0x01) t |= 0x02;
+	if(line_parity_prime & 0x01) t |= 0x01;
+	ecc[0] = ~t;
+
+#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
+	// Swap the bytes into the wrong order
+	t = ecc[0];
+	ecc[0] = ecc[1];
+	ecc[1] = t;
+#endif 
+}
+
+int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, const unsigned char *test_ecc)
+{
+	unsigned char d0, d1, d2; // deltas 
+
+	d0 = read_ecc[0] ^ test_ecc[0];
+	d1 = read_ecc[1] ^ test_ecc[1];
+	d2 = read_ecc[2] ^ test_ecc[2];
+	
+	
+	
+	if((d0 | d1 | d2) == 0)
+	{
+		// no error
+		return 0;
+	}
+	
+	if( ((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
+	    ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
+	    ((d2 ^ (d2 >> 1)) & 0x54) == 0x54)
+	{
+		// Single bit (recoverable) error in data
+
+		unsigned byte;
+		unsigned bit;
+
+#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
+		// swap the bytes to correct for the wrong order
+		unsigned char t;
+		
+		t = d0;
+		d0 = d1;
+		d1 = t;
+#endif
+		
+		bit = byte = 0;
+		
+		
+		if(d1 & 0x80) byte |= 0x80;
+		if(d1 & 0x20) byte |= 0x40;
+		if(d1 & 0x08) byte |= 0x20;
+		if(d1 & 0x02) byte |= 0x10;
+		if(d0 & 0x80) byte |= 0x08;
+		if(d0 & 0x20) byte |= 0x04;
+		if(d0 & 0x08) byte |= 0x02;
+		if(d0 & 0x02) byte |= 0x01;
+
+		if(d2 & 0x80) bit |= 0x04;
+		if(d2 & 0x20) bit |= 0x02;
+		if(d2 & 0x08) bit |= 0x01;
+
+		data[byte] ^= (1 << bit);
+		
+		return 1;
+	}
+	
+	if((yaffs_CountBits(d0)+yaffs_CountBits(d1)+yaffs_CountBits(d2)) == 1)
+	{
+		// Reccoverable error in ecc
+		
+		read_ecc[0] = test_ecc[0];
+		read_ecc[1] = test_ecc[1];
+		read_ecc[2] = test_ecc[2];
+		
+		return 1;
+	}
+	
+	// Unrecoverable error
+	
+	return -1;
+	    
+
+}
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_ecc.h linux-2.4.30/fs/yaffs/yaffs_ecc.h
--- linux-2.4.30-orig/fs/yaffs/yaffs_ecc.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_ecc.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,30 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * yaffs_ecc.c: ECC generation/correction algorithms.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * 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.
+ *
+ */
+ 
+ /*
+ * This code implements the ECC algorithm used in SmartMedia.
+ *
+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. 
+ * The two unused bit are set to 1.
+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC 
+ * blocks are used on a 512-byte NAND page.
+ *
+ */
+
+#ifndef __YAFFS_ECC_H__
+#define __YAFFS_ECC_H__
+void yaffs_ECCCalculate(const unsigned char *data,unsigned char *ecc);
+int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, const unsigned char *test_ecc);
+#endif
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_fileem.c linux-2.4.30/fs/yaffs/yaffs_fileem.c
--- linux-2.4.30-orig/fs/yaffs/yaffs_fileem.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_fileem.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,271 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yaffs_fileem.c  NAND emulation on top of files
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * 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.
+ *
+ */
+ //yaffs_fileem.c
+
+#include "yaffs_fileem.h"
+#include "yaffs_guts.h"
+#include "yaffsinterface.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#define FILE_SIZE_IN_MEG 2
+
+// #define YAFFS_ERROR_TESTING 
+
+#define BLOCK_SIZE (32 * 528)
+#define BLOCKS_PER_MEG ((1024*1024)/(32 * 512))
+#define FILE_SIZE_IN_BLOCKS (FILE_SIZE_IN_MEG * BLOCKS_PER_MEG)
+#define FILE_SIZE_IN_BYTES (FILE_SIZE_IN_BLOCKS * BLOCK_SIZE)
+
+
+static int h;
+static __u8 ffChunk[528];
+
+static int eraseDisplayEnabled;
+
+static int markedBadBlocks[] = { 1, 4, -1};
+
+static int IsAMarkedBadBlock(int blk)
+{
+#if YAFFS_ERROR_TESTING
+	int *m = markedBadBlocks;
+	
+	while(*m >= 0)
+	{
+		if(*m == blk) return 1;
+		m++;
+	}
+#endif
+	return 0;
+}
+
+
+static __u8 yaffs_WriteFailCorruption(int chunkInNAND)
+{
+#if YAFFS_ERROR_TESTING
+
+	// Whole blocks that fail
+	switch(chunkInNAND/YAFFS_CHUNKS_PER_BLOCK)
+	{
+		case 50:
+		case 52:
+					return 7;
+	}
+	
+	// Single blocks that fail
+	switch(chunkInNAND)
+	{
+		case 2000:
+		case 2003:
+		case 3000:
+		case 3001:
+					return 3;// ding two bits
+		case 2001:
+		case 3003:
+		case 3004:
+		case 3005:
+		case 3006:
+		case 3007:  return 1;// ding one bit
+		
+		
+	}
+#endif
+	return 0;
+}
+
+static void yaffs_ModifyWriteData(int chunkInNAND,__u8 *data)
+{
+#if YAFFS_ERROR_TESTING
+	if(data)
+	{
+		*data ^= yaffs_WriteFailCorruption(chunkInNAND);	
+	}
+#endif
+}
+
+static __u8 yaffs_ReadFailCorruption(int chunkInNAND)
+{
+	switch(chunkInNAND)
+	{
+#if YAFFS_ERROR_TESTING
+		case 500:
+					return 3;// ding two bits
+		case 700:
+		case 750:
+					return 1;// ding one bit
+		
+#endif
+		default: return 0;
+		
+	}
+}
+
+static void yaffs_ModifyReadData(int chunkInNAND,__u8 *data)
+{
+#if YAFFS_ERROR_TESTING
+	if(data)
+	{
+		*data ^= yaffs_ReadFailCorruption(chunkInNAND);	
+	}
+#endif
+}
+
+
+
+
+
+static void  CheckInit(yaffs_Device *dev)
+{
+	static int initialised = 0;
+
+	int length;
+	int nWritten;
+
+	
+	if(!initialised)
+	{
+		memset(ffChunk,0xFF,528);
+		
+//#ifdef YAFFS_DUMP
+//		h = open("yaffs-em-file" , O_RDONLY);
+//#else
+		h = open("yaffs-em-file" , O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
+//#endif
+		if(h < 0)
+		{
+			perror("Fatal error opening yaffs emulation file");
+			exit(1);
+		}
+		initialised = 1;
+		
+		length = lseek(h,0,SEEK_END);
+		nWritten = 528;
+		while(length <  FILE_SIZE_IN_BYTES && nWritten == 528)
+		{
+			write(h,ffChunk,528);
+			length = lseek(h,0,SEEK_END);		
+		}
+		if(nWritten != 528)
+		{
+			perror("Fatal error expanding yaffs emulation file");
+			exit(1);
+
+		}
+		
+		close(h);
+		
+#ifdef YAFFS_DUMP
+		h = open("yaffs-em-file" , O_RDONLY);
+#else
+		h = open("yaffs-em-file" , O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
+#endif
+	}
+}
+
+int yaffs_FEWriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_Spare *spare)
+{
+	__u8 localData[512];
+	
+	int pos;
+	
+	pos = chunkInNAND * 528;
+	
+	CheckInit(dev);
+	
+	
+	if(data)
+	{
+		memcpy(localData,data,512);
+		yaffs_ModifyWriteData(chunkInNAND,localData);
+		lseek(h,pos,SEEK_SET);
+		write(h,localData,512);
+	}
+	
+	pos += 512;
+	
+	if(spare)
+	{
+		lseek(h,pos,SEEK_SET);
+		write(h,spare,16);	
+	}
+
+	return YAFFS_OK;
+}
+
+
+int yaffs_FEReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
+{
+	int pos;
+
+	pos = chunkInNAND * 528;
+	
+	
+	CheckInit(dev);
+	
+	if(data)
+	{
+		lseek(h,pos,SEEK_SET);
+		read(h,data,512);
+		yaffs_ModifyReadData(chunkInNAND,data);
+	}
+	
+	pos += 512;
+	
+	if(spare)
+	{
+		lseek(h,pos,SEEK_SET);
+		read(h,spare,16);	
+	}
+
+	return YAFFS_OK;
+}
+
+
+int yaffs_FEEraseBlockInNAND(yaffs_Device *dev,int blockInNAND)
+{
+	int i;
+	
+	CheckInit(dev);
+	
+	if(eraseDisplayEnabled)
+	{
+		printf("Erasing block %d\n",blockInNAND);
+	}
+	
+	lseek(h,blockInNAND * BLOCK_SIZE,SEEK_SET);
+	for(i = 0; i < 32; i++)
+	{
+		write(h,ffChunk,528);
+	}
+	
+	switch(blockInNAND)
+	{
+		case 10: 
+		case 15: return YAFFS_FAIL;
+		
+	}
+	return YAFFS_OK;
+}
+
+int yaffs_FEInitialiseNAND(yaffs_Device *dev)
+{
+	return YAFFS_OK;
+}
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_fs.c linux-2.4.30/fs/yaffs/yaffs_fs.c
--- linux-2.4.30-orig/fs/yaffs/yaffs_fs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_fs.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,1725 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * yaffs_fs.c
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * 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.
+ *
+ * This is the file system front-end to YAFFS that hooks it up to
+ * the VFS.
+ *
+ * Special notes: 
+ * >> sb->u.generic_sbp points to the yaffs_Device associated with this superblock
+ * >> inode->u.generic_ip points to the associated yaffs_Object.
+ *
+ *
+ * Acknowledgements:
+ * * Luc van OostenRyck for numerous patches.
+ * * Nick Bane for numerous patches.
+ * * Nick Bane for 2.5/2.6 integration.
+ * * Andras Toth for mknod rdev issue.
+ * * Michael Fischer for finding the problem with inode inconsistency.
+ * * Some code bodily lifted from JFFS2.
+ */
+
+
+const char *yaffs_fs_c_version = "$Id: yaffs_fs.c,v 1.1 2005/10/13 15:18:46 lel Exp $";
+extern const char *yaffs_guts_c_version;
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+
+#include <linux/statfs.h>	/* Added NCB 15-8-2003 */
+#include <asm/statfs.h>
+#define UnlockPage(p) unlock_page(p)
+#define Page_Uptodate(page)	test_bit(PG_uptodate, &(page)->flags)
+//#define kdevname(x) cdevname(to_kdev_t(x))
+#define kdevname(x) "(unavailable)"	// temporary fix
+
+#else
+
+#include <linux/locks.h>
+
+#endif
+
+
+#include <asm/uaccess.h>
+
+#include "yportenv.h"
+#include "yaffs_guts.h"
+
+
+
+
+unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS;
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+#include "yaffs_nandemul.h" 
+// 2 MB of RAM for emulation
+#define YAFFS_RAM_EMULATION_SIZE  0x200000
+#endif //CONFIG_YAFFS_RAM_ENABLED
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+#include <linux/mtd/mtd.h>
+#include "yaffs_mtdif.h"
+#endif //CONFIG_YAFFS_MTD_ENABLED
+
+//#define T(x) printk x
+
+
+
+#define yaffs_InodeToObject(iptr) ((yaffs_Object *)((iptr)->u.generic_ip))
+#define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode)
+//NCB #define yaffs_SuperToDevice(sb)	((yaffs_Device *)sb->u.generic_sbp)
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+#define yaffs_SuperToDevice(sb)	((yaffs_Device *)sb->s_fs_info)
+#else
+#define yaffs_SuperToDevice(sb)	((yaffs_Device *)sb->u.generic_sbp)
+#endif
+
+
+static void yaffs_put_super(struct super_block *sb);
+
+static ssize_t yaffs_file_read(struct file *f, char *buf, size_t n, loff_t *pos);
+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t *pos);
+static int yaffs_file_flush(struct file* file);
+
+static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync);
+
+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir);
+
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n);
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n);
+#else
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode);
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry);
+#endif
+static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry);
+static int yaffs_unlink(struct inode * dir, struct dentry *dentry);
+static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname);
+static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode);
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
+#else
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev);
+#endif
+static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry);
+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr);
+
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf);
+#else
+static int yaffs_statfs(struct super_block *sb, struct statfs *buf);
+#endif
+static void yaffs_read_inode (struct inode *inode);
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static struct super_block *yaffs_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data);
+#else
+static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent);
+#endif
+
+static void yaffs_put_inode (struct inode *inode);
+static void yaffs_delete_inode(struct inode *);
+static void yaffs_clear_inode(struct inode *);
+
+static int yaffs_readpage(struct file *file, struct page * page);
+static int yaffs_writepage(struct page *page);
+static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to);
+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, unsigned to);
+
+static int yaffs_readlink(struct dentry *dentry, char *buffer, int buflen);
+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd);
+
+
+
+
+static struct address_space_operations yaffs_file_address_operations = {
+	readpage:		yaffs_readpage,
+	writepage:		yaffs_writepage,
+	prepare_write:	yaffs_prepare_write,
+	commit_write:	yaffs_commit_write
+};
+
+
+static struct file_operations yaffs_file_operations = {
+#ifdef CONFIG_YAFFS_USE_GENERIC_RW
+	read:		generic_file_read,
+	write:		generic_file_write,
+#else
+	read:		yaffs_file_read,
+	write:		yaffs_file_write,
+#endif
+	mmap:		generic_file_mmap,
+	flush:		yaffs_file_flush,
+	fsync:		yaffs_sync_object,
+	
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+	sendfile:	generic_file_sendfile,
+#endif
+};
+
+
+static struct inode_operations yaffs_file_inode_operations = {
+	setattr:	yaffs_setattr,
+};
+
+
+struct inode_operations yaffs_symlink_inode_operations =
+{	
+	readlink:	yaffs_readlink,
+	follow_link:	yaffs_follow_link,
+	setattr:	yaffs_setattr
+};
+
+static struct inode_operations yaffs_dir_inode_operations = {
+	create:		yaffs_create,
+	lookup:		yaffs_lookup,
+	link:		yaffs_link,
+	unlink:		yaffs_unlink,	
+	symlink:	yaffs_symlink,
+	mkdir:		yaffs_mkdir,
+	rmdir:		yaffs_unlink,
+	mknod:		yaffs_mknod,
+	rename:		yaffs_rename,
+	setattr:	yaffs_setattr,
+};
+
+static struct file_operations yaffs_dir_operations = {
+	read:		generic_read_dir,
+	readdir:	yaffs_readdir,
+	fsync:		yaffs_sync_object,
+};
+
+
+static struct super_operations yaffs_super_ops = {
+	statfs:			yaffs_statfs,
+	read_inode:		yaffs_read_inode,
+	put_inode:		yaffs_put_inode,
+	put_super:		yaffs_put_super,
+//	remount_fs:
+	delete_inode:		yaffs_delete_inode,
+	clear_inode:		yaffs_clear_inode,
+};
+
+
+
+static void yaffs_GrossLock(yaffs_Device *dev)
+{
+	T(YAFFS_TRACE_OS,("yaffs locking\n"));
+
+	down(&dev->grossLock);
+}
+
+static void yaffs_GrossUnlock(yaffs_Device *dev)
+{
+	T(YAFFS_TRACE_OS,("yaffs unlocking\n"));
+	up(&dev->grossLock);
+
+}
+
+static int yaffs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	unsigned char *alias;
+	int ret;
+
+	yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
+
+
+	yaffs_GrossLock(dev);
+	
+	alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
+	
+	yaffs_GrossUnlock(dev);
+	
+	if(!alias)
+		return -ENOMEM;
+
+	ret = vfs_readlink(dentry, buffer, buflen, alias);
+	kfree(alias);
+	return ret;
+}
+
+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	unsigned char *alias;
+	int ret;
+	yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
+
+
+	yaffs_GrossLock(dev);
+
+	alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
+	
+	yaffs_GrossUnlock(dev);
+	
+	if(!alias)
+		return -ENOMEM;
+
+	ret = vfs_follow_link(nd,alias);
+	kfree(alias);
+	return ret;
+}
+
+
+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj);
+
+/*
+ * Lookup is used to find objects in the fs
+ */
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n)
+#else
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry)
+#endif
+{
+	yaffs_Object *obj;
+	struct inode *inode = NULL; // NCB 2.5/2.6 needs NULL here
+	
+	yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev;
+
+
+	yaffs_GrossLock(dev);
+
+	
+	T(YAFFS_TRACE_OS,("yaffs_lookup for %d:%s\n",yaffs_InodeToObject(dir)->objectId,dentry->d_name.name));
+	
+	obj = yaffs_FindObjectByName(yaffs_InodeToObject(dir),dentry->d_name.name);
+	
+	obj = yaffs_GetEquivalentObject(obj); // in case it was a hardlink
+	
+
+	
+	if(obj)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_lookup found %d\n",obj->objectId));
+		
+		inode = yaffs_get_inode(dir->i_sb, obj->yst_mode,0,obj);
+		
+		if(inode)
+		{
+			T(YAFFS_TRACE_OS,("yaffs_loookup dentry \n"));
+/* #if 0 asserted by NCB for 2.5/6 compatability - falls through to d_add even if NULL inode */	
+#if 0
+			//dget(dentry); // try to solve directory bug
+			d_add(dentry,inode);
+			
+			yaffs_GrossUnlock(dev);
+
+			// return dentry;
+			return NULL;
+#endif
+		}
+
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_lookup not found\n"));
+		
+	}
+	yaffs_GrossUnlock(dev);
+
+/* added NCB for 2.5/6 compatability - forces add even if inode is NULL which creates dentry hash*/	
+	d_add(dentry,inode);
+	
+	return NULL;
+	//	return (ERR_PTR(-EIO));
+	
+}
+
+// For now put inode is just for debugging
+// Put inode is called when the inode **structure** is put.
+static void yaffs_put_inode(struct inode *inode)
+{
+	T(YAFFS_TRACE_OS,("yaffs_put_inode: ino %d, count %d nlink %d\n",
+	                       (int)inode->i_ino, atomic_read(&inode->i_count), inode->i_nlink));
+	
+}
+
+// clear is called to tell the fs to release any per-inode data it holds
+static void yaffs_clear_inode(struct inode *inode)
+{
+	yaffs_Object *obj;
+	yaffs_Device *dev;
+	
+	obj = yaffs_InodeToObject(inode);
+	
+	T(YAFFS_TRACE_OS,("yaffs_clear_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count),
+		obj ? "object exists" : "null object"));	
+
+	if(obj)
+	{
+		dev = obj->myDev;
+		yaffs_GrossLock(dev);
+		
+		// Clear the association between the inode ant the yaffs_Object.
+		obj->myInode = NULL;
+		inode->u.generic_ip = NULL;
+		
+		// If the object freeing was deferred, then the real free happens now.
+		// This should fix the inode inconsistency problem.
+		
+		yaffs_HandleDeferedFree(obj);
+		
+		yaffs_GrossUnlock(dev);
+	}
+	
+	
+}
+
+// delete is called when the link count is zero and the inode
+// is put (ie. nobody wants to know about it anymore, time to
+// delete the file).
+// NB Must call clear_inode()
+static void yaffs_delete_inode(struct inode *inode)
+{
+	yaffs_Object *obj = yaffs_InodeToObject(inode);
+	yaffs_Device *dev;
+
+	T(YAFFS_TRACE_OS,("yaffs_delete_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count),
+		obj ? "object exists" : "null object"));
+	
+	if(obj)
+	{
+	 	dev = obj->myDev;
+		yaffs_GrossLock(dev);
+		yaffs_DeleteFile(obj);
+		yaffs_GrossUnlock(dev);
+	}
+	clear_inode(inode);
+}
+
+
+static int yaffs_file_flush(struct file* file)
+{
+	yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry);
+	
+	yaffs_Device *dev = obj->myDev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_file_flush object %d (%s)\n",obj->objectId,
+				obj->dirty ? "dirty" : "clean"));
+
+	yaffs_GrossLock(dev);
+	
+    yaffs_FlushFile(obj,1);
+
+	yaffs_GrossUnlock(dev);
+
+    return 0;
+}
+
+
+
+static int yaffs_readpage_nolock(struct file *f, struct page * pg)
+{
+	// Lifted from jffs2
+	
+	yaffs_Object *obj;
+	unsigned char *pg_buf;
+	int ret;
+
+	yaffs_Device *dev;
+
+	T(YAFFS_TRACE_OS,("yaffs_readpage at %08x, size %08x\n",
+	              (unsigned)(pg->index << PAGE_CACHE_SHIFT), (unsigned)PAGE_CACHE_SIZE));
+
+	obj  = yaffs_DentryToObject(f->f_dentry);
+
+	dev = obj->myDev;
+	
+	
+	if (!PageLocked(pg))
+                PAGE_BUG(pg);
+
+	pg_buf = kmap(pg);
+	/* FIXME: Can kmap fail? */
+
+	yaffs_GrossLock(dev);
+	
+	ret = yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
+
+	yaffs_GrossUnlock(dev);
+	
+	if(ret >= 0) ret = 0;
+
+	if (ret) {
+		ClearPageUptodate(pg);
+		SetPageError(pg);
+	} else {
+		SetPageUptodate(pg);
+		ClearPageError(pg);
+	}
+
+	flush_dcache_page(pg);
+	kunmap(pg);
+
+
+	T(YAFFS_TRACE_OS,("yaffs_readpage done\n"));
+	return ret;
+}
+
+static int yaffs_readpage_unlock(struct file *f, struct page *pg)
+{
+	int ret = yaffs_readpage_nolock(f,pg);
+	UnlockPage(pg);
+	return ret;
+}
+
+static int yaffs_readpage(struct file *f, struct page * pg)
+{
+	return yaffs_readpage_unlock(f,pg);
+}
+
+// writepage inspired by/stolen from smbfs
+//
+
+static int yaffs_writepage(struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode;
+	unsigned long end_index;
+	char *buffer;
+	yaffs_Object *obj;
+	int nWritten = 0;
+	unsigned nBytes;
+
+	if (!mapping)
+		BUG();
+	inode = mapping->host;
+	if (!inode)
+		BUG();
+
+	end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+
+	/* easy case */
+	if (page->index < end_index)
+	{
+		nBytes = PAGE_CACHE_SIZE;
+	}
+	else
+	{
+		nBytes = inode->i_size & (PAGE_CACHE_SIZE-1);
+	}
+	//  What's happening here?
+	///* OK, are we completely out? */
+	//if (page->index >= end_index+1 || !offset)
+	//	return -EIO;
+
+	get_page(page);
+
+
+	buffer = kmap(page);
+
+	obj = yaffs_InodeToObject(inode);
+	yaffs_GrossLock(obj->myDev);
+
+
+	nWritten = yaffs_WriteDataToFile(obj,buffer,page->index << PAGE_CACHE_SHIFT,nBytes);
+
+	yaffs_GrossUnlock(obj->myDev);
+	
+	kunmap(page);
+	SetPageUptodate(page);
+	UnlockPage(page);
+	put_page(page);
+
+	return (nWritten == nBytes) ? 0  : -ENOSPC;
+}
+
+
+
+static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to)
+{
+
+	T(YAFFS_TRACE_OS,("yaffs_prepair_write\n"));
+	if(!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE))
+		return  yaffs_readpage_nolock(f,pg);    
+
+	return 0;
+	
+}
+
+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, unsigned to)
+{
+
+	void *addr = page_address(pg) + offset;
+	loff_t pos = (((loff_t)pg->index) << PAGE_CACHE_SHIFT) + offset;
+	int nBytes = to - offset;
+	int nWritten;
+	
+	unsigned spos = pos;
+	unsigned saddr = (unsigned)addr;
+
+	T(YAFFS_TRACE_OS,("yaffs_commit_write addr %x pos %x nBytes %d\n",saddr,spos,nBytes));
+	
+	nWritten = yaffs_file_write(f,addr, nBytes, &pos);
+	
+	if(nWritten != nBytes)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_commit_write not same size nWritten %d  nBytes %d\n",nWritten,nBytes));
+		SetPageError(pg);
+		ClearPageUptodate(pg);
+	}
+	else
+	{
+		SetPageUptodate(pg);
+	}
+
+	T(YAFFS_TRACE_OS,("yaffs_commit_write returning %d\n",nWritten));
+	
+	return nWritten;
+
+}
+
+
+
+static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
+{
+	if (inode && obj) 
+	{
+		inode->i_ino = obj->objectId;
+		inode->i_mode = obj->yst_mode;
+		inode->i_uid = obj->yst_uid;
+		inode->i_gid = obj->yst_gid;
+		inode->i_blksize = inode->i_sb->s_blocksize;
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+
+		inode->i_rdev = old_decode_dev(obj->yst_rdev);
+		inode->i_atime.tv_sec = (time_t)(obj->yst_atime);
+		inode->i_atime.tv_nsec = 0;
+		inode->i_mtime.tv_sec = (time_t)obj->yst_mtime;
+		inode->i_mtime.tv_nsec =0;
+		inode->i_ctime.tv_sec = (time_t)obj->yst_ctime;
+		inode->i_ctime.tv_nsec = 0;
+#else
+		inode->i_rdev = obj->yst_rdev;
+		inode->i_atime = obj->yst_atime;
+		inode->i_mtime = obj->yst_mtime;
+		inode->i_ctime = obj->yst_ctime;
+#endif
+		inode->i_size = yaffs_GetObjectFileLength(obj);
+		inode->i_blocks = (inode->i_size + 511) >> 9;
+
+		inode->i_nlink = yaffs_GetObjectLinkCount(obj);
+		
+		T(YAFFS_TRACE_OS,("yaffs_FillInode mode %x uid %d gid %d size %d count %d\n",
+				inode->i_mode, inode->i_uid, inode->i_gid, (int)inode->i_size, atomic_read(&inode->i_count)));
+		
+		switch (obj->yst_mode & S_IFMT) 
+		{
+			default: // fifo, device or socket
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+                               init_special_inode(inode, obj->yst_mode,old_decode_dev(obj->yst_rdev));
+#else
+                                 init_special_inode(inode, obj->yst_mode,(dev_t)(obj->yst_rdev));
+#endif
+				break;
+			case S_IFREG:	// file		
+				inode->i_op = &yaffs_file_inode_operations;
+				inode->i_fop = &yaffs_file_operations;
+				inode->i_mapping->a_ops = &yaffs_file_address_operations;
+				break;
+			case S_IFDIR:	// directory
+				inode->i_op = &yaffs_dir_inode_operations;
+				inode->i_fop = &yaffs_dir_operations;
+				break;
+			case S_IFLNK:	// symlink
+				inode->i_op = &yaffs_symlink_inode_operations;
+				break;
+		}
+		
+		
+		inode->u.generic_ip = obj;
+		obj->myInode = inode;
+		
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_FileInode invalid parameters\n"));
+	}
+
+}
+
+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj)
+{
+	struct inode * inode;
+	
+	T(YAFFS_TRACE_OS,("yaffs_get_inode for object %d\n",obj->objectId));
+
+	inode = iget(sb,obj->objectId);
+
+	// NB Side effect: iget calls back to yaffs_read_inode().
+	// iget also increments the inode's i_count
+	
+	return inode;
+}
+
+static ssize_t yaffs_file_read(struct file *f, char *buf, size_t n, loff_t *pos)
+{
+	yaffs_Object *obj;
+	int nRead,ipos;
+	struct inode *inode;
+	yaffs_Device *dev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_file_read\n"));
+
+	obj  = yaffs_DentryToObject(f->f_dentry);
+	
+	dev = obj->myDev;
+	
+	yaffs_GrossLock(dev);
+	
+	inode = f->f_dentry->d_inode;
+	
+	if (*pos < inode->i_size) 
+	{
+			if (*pos + n > inode->i_size)
+			{
+				n = inode->i_size - *pos;
+			}
+	}
+	else
+	{
+		n = 0;
+	}
+	
+	nRead = yaffs_ReadDataFromFile(obj,buf,*pos,n);
+	if(nRead > 0)
+	{
+		f->f_pos += nRead;
+	}
+	
+	yaffs_GrossUnlock(dev);
+	
+	ipos = *pos;
+	
+	T(YAFFS_TRACE_OS,("yaffs_file_read read %d bytes, %d read at %d\n",n,nRead,ipos));
+	return nRead;
+	
+}
+
+
+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t *pos)
+{
+	yaffs_Object *obj;
+	int nWritten,ipos;
+	struct inode *inode;
+	yaffs_Device *dev;
+	
+	
+	obj  = yaffs_DentryToObject(f->f_dentry);
+	
+	dev = obj->myDev;
+	
+	yaffs_GrossLock(dev);
+
+	inode = f->f_dentry->d_inode;
+
+	if(!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND)
+	{
+		ipos = inode->i_size;
+	}
+	else
+	{
+		ipos = *pos;
+	}
+	
+	
+	if(!obj)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_file_write: hey obj is null!\n"));
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_file_write about to write writing %d bytes to object %d at %d\n",n,obj->objectId,ipos));
+	}
+
+	nWritten = yaffs_WriteDataToFile(obj,buf,ipos,n);
+
+	T(YAFFS_TRACE_OS,("yaffs_file_write writing %d bytes, %d written at %d\n",n,nWritten,ipos));
+	if(nWritten > 0)
+	{
+		ipos += nWritten;
+		*pos = ipos;
+		if(ipos > inode->i_size)
+		{
+			inode->i_size = ipos;
+			inode->i_blocks = (ipos + 511)>>9;
+			
+			T(YAFFS_TRACE_OS,("yaffs_file_write size updated to %d bytes, %d blocks\n",ipos,(int)(inode->i_blocks)));
+		}
+		
+	}
+	yaffs_GrossUnlock(dev);
+	
+	return nWritten != n ? -ENOSPC : nWritten;
+}
+
+
+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
+{
+	yaffs_Object *obj;
+	yaffs_Device *dev;
+	struct inode *inode = f->f_dentry->d_inode;
+	unsigned long offset, curoffs;
+	struct list_head *i;	
+	yaffs_Object *l;
+	
+	char name[YAFFS_MAX_NAME_LENGTH	+1];
+		
+	obj =  yaffs_DentryToObject(f->f_dentry);
+	dev = obj->myDev;
+	
+	yaffs_GrossLock(dev);
+	
+	offset = f->f_pos;
+	
+	T(YAFFS_TRACE_OS,("yaffs_readdir: starting at %d\n",(int)offset));
+	
+	if(offset == 0)
+	{
+        T(YAFFS_TRACE_OS,("yaffs_readdir: entry . ino %d \n",(int)inode->i_ino));
+		if(filldir(dirent,".",1,offset,inode->i_ino,DT_DIR) < 0)
+		{
+			goto out;
+		}
+		offset++;
+		f->f_pos++;
+	}
+	if(offset == 1)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_readdir: entry .. ino %d \n",(int)f->f_dentry->d_parent->d_inode->i_ino));
+		if(filldir(dirent,"..",2,offset,f->f_dentry->d_parent->d_inode->i_ino,DT_DIR) < 0)
+		{
+			goto out;
+		}
+		offset++;
+		f->f_pos++;
+	}
+	
+	curoffs = 1;
+	
+	list_for_each(i,&obj->variant.directoryVariant.children)
+	{
+		curoffs++;
+		if(curoffs >= offset)
+		{		
+			l = list_entry(i, yaffs_Object,siblings);
+			
+			yaffs_GetObjectName(l,name,YAFFS_MAX_NAME_LENGTH+1); 
+			T(YAFFS_TRACE_OS,("yaffs_readdir: %s inode %d\n",name,yaffs_GetObjectInode(l)));
+			
+			if(filldir(dirent,
+					   name,
+					   strlen(name),
+					   offset,
+					   yaffs_GetObjectInode(l),
+					   yaffs_GetObjectType(l))
+					   < 0)
+			{
+				goto up_and_out;
+			}
+			
+			offset++;
+			f->f_pos++;	   
+		}
+	}
+
+  up_and_out:
+  out:
+  
+    yaffs_GrossUnlock(dev);
+    
+	return 0;
+}
+
+
+/*
+ * File creation. Allocate an inode, and we're done..
+ */
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+#else
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+#endif
+{
+	struct inode *inode;
+	
+	yaffs_Object *obj = NULL;
+	yaffs_Device *dev;
+	
+	yaffs_Object *parent = yaffs_InodeToObject(dir);
+	
+	int error = -ENOSPC;
+
+	if(parent)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_mknod: parent object %d type %d\n",
+					 parent->objectId,parent->variantType));
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_mknod: could not get parent object\n"));
+		return -EPERM;
+	}
+	
+	T(YAFFS_TRACE_OS,("yaffs_mknod: making oject for %s, mode %x dev %x\n",
+					dentry->d_name.name, mode,rdev));
+
+	dev = parent->myDev;
+	
+	yaffs_GrossLock(dev);
+
+	switch (mode & S_IFMT) 
+	{
+		default:
+			// Special (socket, fifo, device...)
+			T(YAFFS_TRACE_OS,("yaffs_mknod: making special\n"));
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+                        obj = yaffs_MknodSpecial(parent,dentry->d_name.name,mode,current->uid, current->gid,old_encode_dev(rdev));
+#else
+                        obj = yaffs_MknodSpecial(parent,dentry->d_name.name,mode,current->uid, current->gid,rdev);
+#endif
+			break;
+		case S_IFREG:	// file		
+			T(YAFFS_TRACE_OS,("yaffs_mknod: making file\n"));
+			obj = yaffs_MknodFile(parent,dentry->d_name.name,mode,current->uid, current->gid);
+			break;
+		case S_IFDIR:	// directory
+			T(YAFFS_TRACE_OS,("yaffs_mknod: making directory\n"));
+			obj = yaffs_MknodDirectory(parent,dentry->d_name.name,mode,current->uid, current->gid);
+			break;
+		case S_IFLNK:	// symlink
+			T(YAFFS_TRACE_OS,("yaffs_mknod: making file\n"));
+			obj = NULL; // Do we ever get here?
+			break;
+	}
+	
+	if(obj)
+	{
+		inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);
+		d_instantiate(dentry, inode);
+		T(YAFFS_TRACE_OS,("yaffs_mknod created object %d count = %d\n",obj->objectId,atomic_read(&inode->i_count)));
+		error = 0;
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_mknod failed making object\n"));
+		error = -ENOMEM;
+	}
+
+	yaffs_GrossUnlock(dev);
+
+	return error;
+}
+
+static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+	int retVal;
+	T(YAFFS_TRACE_OS,("yaffs_mkdir\n"));
+	retVal =  yaffs_mknod(dir, dentry, mode | S_IFDIR, 0);
+#if 0
+ // attempt to fix dir bug - didn't work
+	if(!retVal)
+	{
+		dget(dentry);
+	}
+#endif
+	return retVal;
+}
+
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n)
+#else
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode)
+#endif
+{
+	T(YAFFS_TRACE_OS,("yaffs_create\n"));
+	return yaffs_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+
+static int yaffs_unlink(struct inode * dir, struct dentry *dentry)
+{
+	int retVal;
+	
+	yaffs_Device *dev;
+	
+	
+	T(YAFFS_TRACE_OS,("yaffs_unlink %d:%s\n",(int)(dir->i_ino),dentry->d_name.name));
+	
+	dev = yaffs_InodeToObject(dir)->myDev;
+	
+	yaffs_GrossLock(dev);
+	
+	
+	retVal = yaffs_Unlink(yaffs_InodeToObject(dir),dentry->d_name.name);
+	
+	
+	yaffs_GrossUnlock(dev);
+	
+	if( retVal == YAFFS_OK)
+	{
+		dentry->d_inode->i_nlink--;
+		mark_inode_dirty(dentry->d_inode);
+		return 0;
+	}
+	else
+	{
+		return -ENOTEMPTY;
+	}
+}
+
+
+/*
+ * Create a link...
+ */
+static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	yaffs_Object *obj = NULL;
+	yaffs_Object *link=NULL;
+	yaffs_Device *dev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_link\n"));
+	
+	obj = yaffs_InodeToObject(inode);
+	dev = obj->myDev;
+	
+	yaffs_GrossLock(dev);
+
+	if (!S_ISDIR(inode->i_mode))	// Don't link directories
+	{
+		link = yaffs_Link(yaffs_InodeToObject(dir),dentry->d_name.name,obj);
+	}
+	
+
+	if(link)
+	{
+		old_dentry->d_inode->i_nlink =  yaffs_GetObjectLinkCount(obj);
+		d_instantiate(dentry, old_dentry->d_inode);
+		atomic_inc(&old_dentry->d_inode->i_count);
+		T(YAFFS_TRACE_OS,("yaffs_link link count %d i_count %d\n",	
+			old_dentry->d_inode->i_nlink,atomic_read(&old_dentry->d_inode->i_count)));
+	
+	}
+	
+	yaffs_GrossUnlock(dev);
+	
+
+	if(link)
+	{
+	
+		return 0;
+	}
+	
+	
+	return -EPERM; 
+}
+
+
+static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
+{
+	yaffs_Object *obj;
+	yaffs_Device *dev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_symlink\n"));
+	
+	dev = yaffs_InodeToObject(dir)->myDev;
+	yaffs_GrossLock(dev);
+	obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name, 
+							 S_IFLNK | S_IRWXUGO, current->uid, current->gid,
+							 symname);
+	yaffs_GrossUnlock(dev);
+
+	if(obj)
+	{
+
+		struct inode* inode;
+	
+		inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj);
+		d_instantiate(dentry, inode);
+		T(YAFFS_TRACE_OS,("symlink created OK\n"));
+		return 0;
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("symlink not created\n"));
+
+	}
+	
+	return -ENOMEM;
+}
+
+static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync)
+{
+
+	yaffs_Object *obj;
+	yaffs_Device *dev;
+	
+	obj = yaffs_DentryToObject(dentry);
+
+	dev = obj->myDev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_sync_object\n"));
+	yaffs_GrossLock(dev);
+	yaffs_FlushFile(obj,1);
+	yaffs_GrossUnlock(dev);
+	return 0;
+}
+
+/*
+ * The VFS layer already does all the dentry stuff for rename.
+ *
+ * NB: POSIX says you can rename an object over an old object of the same name
+ */
+static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
+{
+	yaffs_Device *dev;
+	int retVal = YAFFS_FAIL;
+	int removed = 0;
+	yaffs_Object *target;
+	
+	dev = yaffs_InodeToObject(old_dir)->myDev;
+
+	yaffs_GrossLock(dev);
+	
+	// Check if the target is an existing directory that is not empty.
+	target = yaffs_FindObjectByName(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+	
+	if(target &&
+	   target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
+	   !list_empty(&target->variant.directoryVariant.children))
+	{
+		retVal = YAFFS_FAIL;
+	}
+	else
+	{
+	   
+		// Unlink the target if it exists
+		removed = yaffs_Unlink(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+
+	
+		retVal =  yaffs_RenameObject(yaffs_InodeToObject(old_dir),old_dentry->d_name.name,
+									yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+									
+	}
+	yaffs_GrossUnlock(dev);
+	
+	if(retVal == YAFFS_OK)
+	{
+		if(removed == YAFFS_OK)
+		{
+			new_dentry->d_inode->i_nlink--;
+			mark_inode_dirty(new_dentry->d_inode);
+		}
+		
+		return 0;
+	}
+	else
+	{
+		return -ENOTEMPTY;
+	}
+	
+
+}
+
+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+	yaffs_Device *dev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_setattr of object %d\n",yaffs_InodeToObject(inode)->objectId));
+	
+	if((error = inode_change_ok(inode,attr)) == 0)
+	{
+	
+		dev = yaffs_InodeToObject(inode)->myDev;
+		yaffs_GrossLock(dev);
+		if(yaffs_SetAttributes(yaffs_InodeToObject(inode),attr) == YAFFS_OK)
+		{
+			error = 0;
+		}
+		else
+		{
+			error = -EPERM;
+		}
+		yaffs_GrossUnlock(dev);
+		error = inode_setattr(inode,attr);
+	}
+	return error;
+}
+
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf)
+#else
+static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
+#endif
+{
+	yaffs_Device *dev = yaffs_SuperToDevice(sb);
+	T(YAFFS_TRACE_OS,("yaffs_statfs\n"));
+
+	yaffs_GrossLock(dev);
+	
+	buf->f_type = YAFFS_MAGIC;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_namelen = 255;
+	buf->f_blocks = (dev->endBlock - dev->startBlock + 1) * YAFFS_CHUNKS_PER_BLOCK/
+						(sb->s_blocksize/YAFFS_BYTES_PER_CHUNK);
+	buf->f_files = 0;
+	buf->f_ffree = 0;
+	buf->f_bfree = yaffs_GetNumberOfFreeChunks(dev)/
+						(sb->s_blocksize/YAFFS_BYTES_PER_CHUNK);
+	buf->f_bavail =  buf->f_bfree;
+	
+	yaffs_GrossUnlock(dev);
+	return 0;
+}
+
+static void yaffs_read_inode (struct inode *inode)
+{
+	// NB This is called as a side effect of other functions and
+	// thus gross locking should always be in place already.
+	
+	yaffs_Object *obj ; 
+	yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb);
+	
+	T(YAFFS_TRACE_OS,("yaffs_read_inode for %d\n",(int)inode->i_ino));
+
+	obj  = yaffs_FindObjectByNumber(dev,inode->i_ino);
+	
+	yaffs_FillInodeFromObject(inode,obj);
+
+}
+
+
+
+static yaffs_Device *yaffs_dev;
+static yaffs_Device *yaffsram_dev;
+
+
+
+static void yaffs_put_super(struct super_block *sb)
+{
+	yaffs_Device *dev = yaffs_SuperToDevice(sb);
+	
+	yaffs_GrossLock(dev);
+	if(dev->putSuperFunc)
+	{
+		 dev->putSuperFunc(sb);
+	}
+	yaffs_Deinitialise(dev);
+	yaffs_GrossUnlock(dev);
+
+	if(dev == yaffs_dev) yaffs_dev = NULL;
+	if(dev == yaffsram_dev) yaffsram_dev = NULL;
+		
+	kfree(dev);
+}
+
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+
+static void  yaffs_MTDPutSuper(struct super_block *sb)
+{
+	
+	struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice;
+	
+	if(mtd->sync)
+	{
+		mtd->sync(mtd);
+	}
+	
+	put_mtd_device(mtd);
+}
+
+#endif
+
+
+static struct super_block *yaffs_internal_read_super(int useRam, struct super_block * sb, void * data, int silent)
+{
+	int nBlocks;
+	struct inode * inode;
+	struct dentry * root;
+	yaffs_Device *dev;
+	
+	sb->s_magic = YAFFS_MAGIC;
+	sb->s_op = &yaffs_super_ops;
+	
+	if(!sb)
+		T(YAFFS_TRACE_OS,("yaffs: sb is NULL\n"));
+	else if(!sb->s_dev)
+		T(YAFFS_TRACE_OS,("yaffs: sb->s_dev is NULL\n"));
+	else if(! kdevname(sb->s_dev))
+		T(YAFFS_TRACE_OS,("yaffs: kdevname is NULL\n"));
+	else
+		T(YAFFS_TRACE_OS,("yaffs: dev is %d name is \"%s\"\n", sb->s_dev, kdevname(sb->s_dev)));
+
+	
+
+#ifdef CONFIG_YAFFS_USE_CHUNK_SIZE
+	sb->s_blocksize = YAFFS_BYTES_PER_CHUNK;
+	sb->s_blocksize_bits = YAFFS_CHUNK_SIZE_SHIFT;
+#else
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+#endif
+	T(YAFFS_TRACE_OS,("yaffs_read_super: %s block size %d\n", useRam ? "RAM" : "MTD",(int)(sb->s_blocksize)));
+
+#ifdef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
+	T(YAFFS_TRACE_OS,("yaffs: Write verification disabled. All guarantees null and void\n"));
+#endif
+
+
+	
+	if(useRam)
+	{
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+		// Set the yaffs_Device up for ram emulation
+
+		sb->u.generic_sbp =	dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
+		if(!dev)
+		{
+			// Deep shit could not allocate device structure
+			T(YAFFS_TRACE_OS,("yaffs_read_super: Failed trying to allocate yaffs_Device. Terminating debug.\n"));
+			return NULL;
+		}
+
+		memset(dev,0,sizeof(yaffs_Device));
+		dev->genericDevice = NULL; // Not used for RAM emulation.
+
+		nBlocks = YAFFS_RAM_EMULATION_SIZE / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
+		dev->startBlock = 0;
+		dev->endBlock = nBlocks - 1;
+		dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+		dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+		dev->nReservedBlocks = 5;
+		dev->nShortOpCaches = 10; // Enable short op caching
+
+		dev->writeChunkToNAND = nandemul_WriteChunkToNAND;
+		dev->readChunkFromNAND = nandemul_ReadChunkFromNAND;
+		dev->eraseBlockInNAND = nandemul_EraseBlockInNAND;
+		dev->initialiseNAND = nandemul_InitialiseNAND;
+
+		yaffsram_dev = dev;
+		
+#endif
+
+	}
+	else
+	{	
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+		struct mtd_info *mtd;
+		
+		T(YAFFS_TRACE_ALWAYS,( "yaffs: Attempting MTD mount on %u.%u, \"%s\"\n",
+		 MAJOR(sb->s_dev),MINOR(sb->s_dev),kdevname(sb->s_dev)));
+			
+		// Check it's an mtd device.....
+		if(MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR)
+		{
+			return NULL; // This isn't an mtd device
+		} 
+		
+		// Get the device
+		mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
+		if (!mtd) 
+		{
+			T(YAFFS_TRACE_ALWAYS,( "yaffs: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
+			return NULL;
+		}
+		
+		// Check it's NAND
+		if(mtd->type != MTD_NANDFLASH)
+		{
+			T(YAFFS_TRACE_ALWAYS,( "yaffs: MTD device is not NAND it's type %d\n", mtd->type));
+			return NULL;
+		}
+
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" erase %x\n",mtd->erase);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" read %x\n",mtd->read);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" write %x\n",mtd->write);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" readoob %x\n",mtd->read_oob);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" writeoob %x\n",mtd->write_oob);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" oobblock %x\n",mtd->oobblock);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" oobsize %x\n",mtd->oobsize);
+
+
+		if(!mtd->erase ||
+		   !mtd->read  ||
+		   !mtd->write ||
+#ifndef CONFIG_YAFFS_USE_OLD_MTD
+		   !mtd->write_ecc ||
+		   !mtd->read_ecc ||
+#endif
+		   !mtd->read_oob ||
+		   !mtd->write_oob )
+		{
+			T(YAFFS_TRACE_ALWAYS,( "yaffs: MTD device does not support required functions\n"));
+			return NULL;
+		}
+		
+		if(mtd->oobblock != YAFFS_BYTES_PER_CHUNK ||
+		   mtd->oobsize != YAFFS_BYTES_PER_SPARE)
+		{
+			T(YAFFS_TRACE_ALWAYS,( "yaffs: MTD device does not support have the right page sizes\n"));
+			return NULL;
+		}
+		   
+
+		// OK, so if we got here, we have an MTD that's NAND and looks 
+		// like it has the right capabilities
+		// Set the yaffs_Device up for ram emulation
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+		sb->s_fs_info =	dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
+#else
+		sb->u.generic_sbp =	dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
+#endif
+		if(!dev)
+		{
+			// Deep shit could not allocate device structure
+			T(YAFFS_TRACE_ALWAYS,("yaffs_read_super: Failed trying to allocate yaffs_Device. Terminating debug.\n"));
+			return NULL;
+		}
+
+		memset(dev,0,sizeof(yaffs_Device));
+		dev->genericDevice = mtd; 
+
+		// Set up the memory size parameters....
+		
+		nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
+		dev->startBlock = 0;
+		dev->endBlock = nBlocks - 1;
+		dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+		dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+		dev->nReservedBlocks = 5;
+		dev->nShortOpCaches = 10; // Enable short op caching
+		
+
+		// ... and the functions.
+		dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
+		dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
+		dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
+		dev->initialiseNAND = nandmtd_InitialiseNAND;
+				
+		dev->putSuperFunc = yaffs_MTDPutSuper;
+		
+#ifdef CONFIG_YAFFS_USE_NANDECC
+		dev->useNANDECC = 1;
+#endif
+
+		yaffs_dev = dev;
+		
+#endif
+	}
+
+	init_MUTEX(&dev->grossLock);
+	
+	
+	yaffs_GrossLock(dev);
+	yaffs_GutsInitialise(yaffs_SuperToDevice(sb));
+
+	T(YAFFS_TRACE_OS,("yaffs_read_super: guts initialised\n"));
+
+	// Create root inode
+	inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0,yaffs_Root(yaffs_SuperToDevice(sb)));
+
+	yaffs_GrossUnlock(dev);
+
+	if (!inode)
+		return NULL;
+		
+// added NCB
+	inode->i_op = & yaffs_dir_inode_operations;
+	inode->i_fop = & yaffs_dir_operations;
+
+	T(YAFFS_TRACE_OS,("yaffs_read_super: got root inode\n"));
+		
+
+	root = d_alloc_root(inode);
+
+	T(YAFFS_TRACE_OS,("yaffs_read_super: d_alloc_root done\n"));
+
+	if (!root) {
+		iput(inode);
+		return NULL;
+	}
+	sb->s_root = root;
+
+	T(YAFFS_TRACE_OS,("yaffs_read_super: done\n"));
+	return sb;
+}
+
+static int yaffs_internal_read_super_ram(struct super_block * sb, void * data, int silent)
+{
+	 return yaffs_internal_read_super(1,sb,data,silent) ? 0 : -1;
+}
+static int yaffs_internal_read_super_mtd(struct super_block * sb, void * data, int silent)
+{
+	 return yaffs_internal_read_super(0,sb,data,silent) ? 0 : -1;
+}
+
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static struct super_block *yaffs_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
+{
+
+    return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_mtd);
+}
+
+/* changes NCB 2.5.70 */
+//static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
+static struct file_system_type yaffs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "yaffs",
+	.get_sb		= yaffs_read_super,
+	.kill_sb	= kill_block_super,
+//	.kill_sb	= kill_litter_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+#else
+static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent)
+{
+	return yaffs_internal_read_super(0,sb,data,silent);
+}
+
+static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
+#endif
+#endif
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static struct super_block *yaffs_ram_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
+{
+
+    return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_ram);
+}
+
+/* changes NCB 2.5.70 */
+//static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
+static struct file_system_type yaffs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "yaffsram",
+	.get_sb		= yaffs_ram_read_super,
+	.kill_sb	= kill_block_super,
+//	.kill_sb	= kill_litter_super,
+	.fs_flags	= FS_SINGLE,
+};
+#else
+static struct super_block *yaffs_ram_read_super(struct super_block * sb, void * data, int silent)
+{
+	return yaffs_internal_read_super(1,sb,data,silent);
+}
+
+static DECLARE_FSTYPE(yaffs_ram_fs_type, "yaffsram", yaffs_ram_read_super, FS_SINGLE);
+#endif
+#endif // CONFIG_YAFFS_RAM_ENABLED
+
+
+static struct proc_dir_entry *my_proc_entry;
+static struct proc_dir_entry *my_proc_ram_write_entry;
+
+static char * yaffs_dump_dev(char *buf,yaffs_Device *dev,char *name)
+{
+	buf +=sprintf(buf,"\nDevice %s\n",name);
+	buf +=sprintf(buf,"startBlock......... %d\n",dev->startBlock);
+	buf +=sprintf(buf,"endBlock........... %d\n",dev->endBlock);
+	buf +=sprintf(buf,"chunkGroupBits..... %d\n",dev->chunkGroupBits);
+	buf +=sprintf(buf,"chunkGroupSize..... %d\n",dev->chunkGroupSize);
+	buf +=sprintf(buf,"nErasedBlocks...... %d\n",dev->nErasedBlocks);
+	buf +=sprintf(buf,"nTnodesCreated..... %d\n",dev->nTnodesCreated);
+	buf +=sprintf(buf,"nFreeTnodes........ %d\n",dev->nFreeTnodes);
+	buf +=sprintf(buf,"nObjectsCreated.... %d\n",dev->nObjectsCreated);
+	buf +=sprintf(buf,"nFreeObjects....... %d\n",dev->nFreeObjects);
+	buf +=sprintf(buf,"nFreeChunks........ %d\n",dev->nFreeChunks);
+	buf +=sprintf(buf,"nPageWrites........ %d\n",dev->nPageWrites);
+	buf +=sprintf(buf,"nPageReads......... %d\n",dev->nPageReads);
+	buf +=sprintf(buf,"nBlockErasures..... %d\n",dev->nBlockErasures);
+	buf +=sprintf(buf,"nGCCopies.......... %d\n",dev->nGCCopies);
+	buf +=sprintf(buf,"garbageCollections. %d\n",dev->garbageCollections);
+	buf +=sprintf(buf,"passiveGCs......... %d\n",dev->passiveGarbageCollections);
+	buf +=sprintf(buf,"nRetriedWrites..... %d\n",dev->nRetriedWrites);
+	buf +=sprintf(buf,"nRetireBlocks...... %d\n",dev->nRetiredBlocks);
+	buf +=sprintf(buf,"eccFixed........... %d\n",dev->eccFixed);
+	buf +=sprintf(buf,"eccUnfixed......... %d\n",dev->eccUnfixed);
+	buf +=sprintf(buf,"tagsEccFixed....... %d\n",dev->tagsEccFixed);
+	buf +=sprintf(buf,"tagsEccUnfixed..... %d\n",dev->tagsEccUnfixed);
+	buf +=sprintf(buf,"cacheHits.......... %d\n",dev->cacheHits);
+	buf +=sprintf(buf,"nDeletedFiles...... %d\n",dev->nDeletedFiles);
+	buf +=sprintf(buf,"nUnlinkedFiles..... %d\n",dev->nUnlinkedFiles);
+	buf +=sprintf(buf,"nBackgroudDeletions %d\n",dev->nBackgroundDeletions);
+	buf +=sprintf(buf,"useNANDECC......... %d\n",dev->useNANDECC);
+	buf +=sprintf(buf,"nShortOpCaches..... %d\n",dev->nShortOpCaches);
+
+	
+	return buf;	
+}
+
+static int  yaffs_proc_read(
+        char *page,
+	char **start,
+	off_t offset,
+	int count,
+	int *eof,
+	void *data
+	)
+{
+
+	char my_buffer[3000];
+	char *buf;
+	buf = my_buffer;
+
+	if (offset > 0) return 0;
+
+	/* Fill the buffer and get its length */
+	buf +=sprintf(buf,"YAFFS built:"__DATE__ " "__TIME__"\n%s\n%s\n", yaffs_fs_c_version,yaffs_guts_c_version);
+	
+	if(yaffs_dev) buf = yaffs_dump_dev(buf,yaffs_dev,"yaffs");
+	if(yaffsram_dev) buf = yaffs_dump_dev(buf,yaffsram_dev,"yaffsram");
+	
+
+	strcpy(page,my_buffer);
+	return strlen(my_buffer);
+}
+
+
+static int  yaffs_proc_ram_write(
+        char *page,
+	char **start,
+	off_t offset,
+	int count,
+	int *eof,
+	void *data
+	)
+{
+
+	T(YAFFS_TRACE_OS,( "yaffs write size %d\n",count));
+	return count;
+}
+
+static int __init init_yaffs_fs(void)
+{
+	int error = 0;
+	
+	yaffs_dev = yaffsram_dev = NULL;
+	
+	T(YAFFS_TRACE_OS,( "yaffs " __DATE__ " " __TIME__ " Initialisation\n"));
+#ifdef CONFIG_YAFFS_USE_GENERIC_RW
+	T(YAFFS_TRACE_OS,( "yaffs is using generic read/write (caching)\n"));
+#else
+	T(YAFFS_TRACE_OS,( "yaffs is using direct read/write (uncached)\n"));
+#endif
+
+
+    /* Install the proc_fs entry */
+    my_proc_entry = create_proc_read_entry("yaffs",
+                                           S_IRUGO | S_IFREG,
+					   &proc_root,
+					   yaffs_proc_read,
+					   NULL);
+    if(!my_proc_entry)
+    {
+       return -ENOMEM;
+    }
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+#if 0
+    my_proc_ram_write_entry = create_proc_entry("yaffs_ram",
+                                           S_IRUGO | S_IFREG,
+					   &proc_root);
+ 
+    if(!my_proc_ram_write_entry)
+    {
+       return -ENOMEM;
+    }
+    else
+    {
+    	my_proc_ram_write_entry->write_proc = yaffs_proc_ram_write;
+    }
+#endif
+
+    error = register_filesystem(&yaffs_ram_fs_type);
+    if(error)
+    {
+    	return error;
+    }
+#endif //CONFIG_YAFFS_RAM_ENABLED
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+	error = register_filesystem(&yaffs_fs_type);
+	if(error)
+	{
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+		unregister_filesystem(&yaffs_ram_fs_type);
+#endif //CONFIG_YAFFS_RAM_ENABLED
+	}
+#endif // CONFIG_YAFFS_MTD_ENABLED
+
+	return error;
+}
+
+static void __exit exit_yaffs_fs(void)
+{
+	T(YAFFS_TRACE_OS,( "yaffs " __DATE__ " " __TIME__ " Clean up\n"));
+
+    remove_proc_entry("yaffs",&proc_root);
+    
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+	unregister_filesystem(&yaffs_ram_fs_type);
+#endif
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+	unregister_filesystem(&yaffs_fs_type);
+#endif
+
+}
+
+module_init(init_yaffs_fs)
+module_exit(exit_yaffs_fs)
+
+MODULE_DESCRIPTION("YAFFS - a NAND specific flash file system");
+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002");
+MODULE_LICENSE("GPL");
+
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_guts.c linux-2.4.30/fs/yaffs/yaffs_guts.c
--- linux-2.4.30-orig/fs/yaffs/yaffs_guts.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_guts.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,5041 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * yaffs_guts.c  The main guts of YAFFS
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * 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.
+ *
+ */
+ //yaffs_guts.c
+
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.1 2005/10/13 15:18:46 lel Exp $";
+
+#include "yportenv.h"
+
+#include "yaffsinterface.h"
+#include "yaffs_guts.h"
+
+#define YAFFS_PASSIVE_GC_CHUNKS 2
+
+#if 0
+// Use Steven Hill's ECC struff instead
+// External functions for ECC on data
+void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
+int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+#define yaffs_ECCCalculate(data,ecc) nand_calculate_ecc(data,ecc)
+#define yaffs_ECCCorrect(data,read_ecc,calc_ecc) nand_correct_ecc(data,read_ecc,calc_ecc)
+#else
+#include "yaffs_ecc.h"
+#endif
+
+// countBits is a quick way of counting the number of bits in a byte.
+// ie. countBits[n] holds the number of 1 bits in a byte with the value n.
+
+static const char yaffs_countBitsTable[256] =
+{
+0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
+};
+
+static int yaffs_CountBits(__u8 x)
+{
+	int retVal;
+	retVal = yaffs_countBitsTable[x];
+	return retVal;
+}
+
+
+
+// Local prototypes
+static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);
+static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);
+static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan);
+
+static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);
+static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force);
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND);
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
+static int yaffs_CheckStructures(void);
+static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit);
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in);
+
+static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev,int blockNo);
+
+// Robustification
+static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND);
+static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND);
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND);
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare);
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare);
+
+static int  yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND);
+
+static int yaffs_UnlinkWorker(yaffs_Object *obj);
+static void yaffs_AbortHalfCreatedObject(yaffs_Object *obj);
+
+static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1);
+
+
+
+loff_t yaffs_GetFileSize(yaffs_Object *obj);
+
+static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted);
+static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted);
+
+
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
+
+#ifdef YAFFS_PARANOID
+static int yaffs_CheckFileSanity(yaffs_Object *in);
+#else
+#define yaffs_CheckFileSanity(in)
+#endif
+
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in);
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId);
+
+// Chunk bitmap manipulations
+
+static __inline __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk)
+{
+	if(blk < dev->internalStartBlock || blk > dev->internalEndBlock)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),blk));
+		YBUG();
+	}
+	return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->internalStartBlock));
+}
+
+static __inline__ void yaffs_ClearChunkBits(yaffs_Device *dev,int blk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+	 memset(blkBits,0,dev->chunkBitmapStride);
+}
+
+static __inline__ void yaffs_ClearChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+	
+	blkBits[chunk/8] &=  ~ (1<<(chunk & 7));
+}
+
+static __inline__ void yaffs_SetChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+	blkBits[chunk/8] |=   (1<<(chunk & 7));
+}
+
+static __inline__ int yaffs_CheckChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+	return (blkBits[chunk/8] &  (1<<(chunk & 7))) ? 1 :0;
+}
+
+static __inline__ int yaffs_StillSomeChunkBits(yaffs_Device *dev,int blk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+	int i;
+	for(i = 0; i < dev->chunkBitmapStride; i++)
+	{
+		if(*blkBits) return 1;
+		blkBits++;
+	}
+	return 0;
+}
+
+// Function to manipulate block info
+static  __inline__ yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk)
+{
+	if(blk < dev->internalStartBlock || blk > dev->internalEndBlock)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),blk));
+		YBUG();
+	}
+	return &dev->blockInfo[blk - dev->internalStartBlock];
+}
+
+
+static  __inline__ int yaffs_HashFunction(int n)
+{
+	return (n % YAFFS_NOBJECT_BUCKETS);
+}
+
+
+yaffs_Object *yaffs_Root(yaffs_Device *dev)
+{
+	return dev->rootDir;
+}
+
+yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)
+{
+	return dev->lostNFoundDir;
+}
+
+
+static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
+{
+	if(chunkInNAND < dev->internalStartBlock * dev->nChunksPerBlock)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));
+		return YAFFS_FAIL;
+	}
+
+	dev->nPageWrites++;
+	return dev->writeChunkToNAND(dev,chunkInNAND - dev->chunkOffset,data,spare);
+}
+
+
+
+int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
+							int chunkInNAND, 
+							__u8 *data, 
+							yaffs_Spare *spare, 
+							int doErrorCorrection)
+{
+	int retVal;
+	yaffs_Spare localSpare;
+
+	dev->nPageReads++;
+	
+	
+
+	
+	if(!spare && data)
+	{
+		// If we don't have a real spare, then we use a local one.
+		// Need this for the calculation of the ecc
+		spare = &localSpare;
+	}
+	
+
+	if(!dev->useNANDECC)
+	{
+		retVal  = dev->readChunkFromNAND(dev,chunkInNAND - dev->chunkOffset,data,spare);
+		if(data && doErrorCorrection)
+		{
+			// Do ECC correction
+			//Todo handle any errors
+         	int eccResult1,eccResult2;
+        	__u8 calcEcc[3];
+                
+			yaffs_ECCCalculate(data,calcEcc);
+			eccResult1 = yaffs_ECCCorrect (data,spare->ecc1, calcEcc);
+			yaffs_ECCCalculate(&data[256],calcEcc);
+			eccResult2 = yaffs_ECCCorrect(&data[256],spare->ecc2, calcEcc);
+
+			if(eccResult1>0)
+			{
+				T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND - dev->chunkOffset));
+				dev->eccFixed++;
+			}
+			else if(eccResult1<0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND - dev->chunkOffset));
+				dev->eccUnfixed++;
+			}
+
+			if(eccResult2>0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND - dev->chunkOffset));
+				dev->eccFixed++;
+			}
+			else if(eccResult2<0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND - dev->chunkOffset));
+				dev->eccUnfixed++;
+			}
+
+			if(eccResult1 || eccResult2)
+			{
+				// Hoosterman, we had a data problem on this page
+				yaffs_HandleReadDataError(dev,chunkInNAND);
+			}
+		}
+	}
+	else
+	{
+        // Must allocate enough memory for spare+2*sizeof(int) for ecc results from device.
+    	struct yaffs_NANDSpare nspare;
+		retVal  = dev->readChunkFromNAND(dev,chunkInNAND - dev->chunkOffset,data,(yaffs_Spare*)&nspare);
+		memcpy (spare, &nspare, sizeof(yaffs_Spare));
+		if(data && doErrorCorrection)
+		{
+			if(nspare.eccres1>0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND - dev->chunkOffset));
+			}
+			else if(nspare.eccres1<0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND - dev->chunkOffset));
+			}
+
+			if(nspare.eccres2>0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND - dev->chunkOffset));
+			}
+			else if(nspare.eccres2<0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND - dev->chunkOffset));
+			}
+
+			if(nspare.eccres1 || nspare.eccres2)
+			{
+				// Hoosterman, we had a data problem on this page
+				yaffs_HandleReadDataError(dev,chunkInNAND);
+			}
+
+		}
+	}
+	return retVal;
+}
+
+
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)
+{
+
+	static int init = 0;
+	static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
+	static __u8 data[YAFFS_BYTES_PER_CHUNK];
+    // Might as well always allocate the larger size for dev->useNANDECC == true;
+	static __u8 spare[sizeof(struct yaffs_NANDSpare)];	
+
+  	dev->readChunkFromNAND(dev,chunkInNAND - dev->chunkOffset,data,(yaffs_Spare *)spare);
+	
+	if(!init)
+	{
+		memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK);
+		init = 1;
+	}
+	
+	if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) return  YAFFS_FAIL;
+	if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL;
+
+	
+	return YAFFS_OK;
+	
+}
+
+
+
+int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
+{
+	dev->nBlockErasures++;
+	return dev->eraseBlockInNAND(dev,blockInNAND - dev->blockOffset);
+}
+
+int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
+{
+	return dev->initialiseNAND(dev);
+}
+
+static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve)
+{
+	int chunk;
+	
+	int writeOk = 1;
+	int attempts = 0;
+	
+	unsigned char rbData[YAFFS_BYTES_PER_CHUNK];
+	yaffs_Spare rbSpare;
+	
+	do{
+		chunk = yaffs_AllocateChunk(dev,useReserve);
+	
+		if(chunk >= 0)
+		{
+
+			// First check this chunk is erased...
+#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
+			writeOk = yaffs_CheckChunkErased(dev,chunk);
+#endif		
+			if(!writeOk)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));
+			}
+			else
+			{
+				writeOk =  yaffs_WriteChunkToNAND(dev,chunk,data,spare);
+			}
+			attempts++;
+			if(writeOk)
+			{
+				// Readback & verify
+				// If verify fails, then delete this chunk and try again
+				// To verify we compare everything except the block and 
+				// page status bytes.
+				// NB We check a raw read without ECC correction applied
+				yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0);
+				
+#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
+				if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare))
+				{
+					// Didn't verify
+					T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk));
+
+					writeOk = 0;
+				}	
+#endif				
+				
+			}
+			if(writeOk)
+			{
+				// Copy the data into the write buffer.
+				// NB We do this at the end to prevent duplicates in the case of a write error.
+				//Todo
+				yaffs_HandleWriteChunkOk(dev,chunk,data,spare);
+			}
+			else
+			{
+				yaffs_HandleWriteChunkError(dev,chunk);
+			}
+		}
+		
+	} while(chunk >= 0 && ! writeOk);
+	
+	if(attempts > 1)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));
+		dev->nRetriedWrites+= (attempts - 1);	
+	}
+	
+	return chunk;
+}
+
+///
+// Functions for robustisizing
+//
+//
+
+static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND)
+{
+	// Ding the blockStatus in the first two pages of the block.
+	
+	yaffs_Spare spare;
+
+	memset(&spare, 0xff,sizeof(yaffs_Spare));
+
+	spare.blockStatus = 0;
+	
+	// TODO change this retirement marking for other NAND types
+	yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL , &spare);
+	yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, NULL , &spare);
+	
+	yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;
+	dev->nRetiredBlocks++;
+}
+
+
+
+static int yaffs_RewriteBufferedBlock(yaffs_Device *dev)
+{
+	dev->doingBufferedBlockRewrite = 1;
+	//
+	//	Remove erased chunks
+	//  Rewrite existing chunks to a new block
+	//	Set current write block to the new block
+	
+	dev->doingBufferedBlockRewrite = 0;
+	
+	return 1;
+}
+
+
+static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)
+{
+	int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
+
+	// Mark the block for retirement
+	yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
+	T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));
+
+
+	//TODO	
+	// Just do a garbage collection on the affected block then retire the block
+	// NB recursion
+}
+
+
+static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND)
+{
+}
+
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare)
+{
+}
+
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare)
+{
+}
+
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)
+{
+	int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
+
+	// Mark the block for retirement
+	yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
+	// Delete the chunk
+	yaffs_DeleteChunk(dev,chunkInNAND,1);
+}
+
+
+
+
+static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1)
+{
+
+
+	if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 ||
+		s0->tagByte0 != s1->tagByte0 ||
+		s0->tagByte1 != s1->tagByte1 ||
+		s0->tagByte2 != s1->tagByte2 ||
+		s0->tagByte3 != s1->tagByte3 ||
+		s0->tagByte4 != s1->tagByte4 ||
+		s0->tagByte5 != s1->tagByte5 ||
+		s0->tagByte6 != s1->tagByte6 ||
+		s0->tagByte7 != s1->tagByte7 ||
+		s0->ecc1[0]  != s1->ecc1[0]  ||
+		s0->ecc1[1]  != s1->ecc1[1]  ||
+		s0->ecc1[2]  != s1->ecc1[2]  ||
+		s0->ecc2[0]  != s1->ecc2[0]  ||
+		s0->ecc2[1]  != s1->ecc2[1]  ||
+		s0->ecc2[2]  != s1->ecc2[2] )
+		{
+			return 0;
+		}
+	
+	return 1;
+}
+
+
+///////////////////////// Object management //////////////////
+// List of spare objects
+// The list is hooked together using the first pointer
+// in the object
+
+// static yaffs_Object *yaffs_freeObjects = NULL;
+
+// static int yaffs_nFreeObjects;
+
+// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL;
+
+// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS];
+
+
+static __u16 yaffs_CalcNameSum(const char *name)
+{
+	__u16 sum = 0;
+	__u16 i = 1;
+	
+	__u8 *bname = (__u8 *)name;
+	if(bname)
+	{
+		while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))
+		{
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+			sum += toupper(*bname) * i;
+#else
+			sum += (*bname) * i;
+#endif
+			i++;
+			bname++;
+		}
+	}
+	return sum;
+}
+
+void yaffs_SetObjectName(yaffs_Object *obj, const char *name)
+{
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+					if(name && strlen(name) <= YAFFS_SHORT_NAME_LENGTH)
+					{
+						strcpy(obj->shortName,name);
+					}
+					else
+					{
+						obj->shortName[0]='\0';
+					}
+#endif
+					obj->sum = yaffs_CalcNameSum(name);
+}
+
+void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
+{
+	yaffs_ECCCalculate(data , spare->ecc1);
+	yaffs_ECCCalculate(&data[256] , spare->ecc2);
+}
+
+void yaffs_CalcTagsECC(yaffs_Tags *tags)
+{
+	// Calculate an ecc
+	
+	unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
+	unsigned  i,j;
+	unsigned  ecc = 0;
+	unsigned bit = 0;
+
+	tags->ecc = 0;
+	
+	for(i = 0; i < 8; i++)
+	{
+		for(j = 1; j &0xff; j<<=1)
+		{
+			bit++;
+			if(b[i] & j)
+			{
+				ecc ^= bit;
+			}
+		}
+	}
+	
+	tags->ecc = ecc;
+	
+	
+}
+
+int  yaffs_CheckECCOnTags(yaffs_Tags *tags)
+{
+	unsigned ecc = tags->ecc;
+	
+	yaffs_CalcTagsECC(tags);
+	
+	ecc ^= tags->ecc;
+	
+	if(ecc && ecc <= 64)
+	{
+		// TODO: Handle the failure better. Retire?
+		unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
+
+		ecc--;
+				
+		b[ecc / 8] ^= (1 << (ecc & 7));
+		
+		// Now recvalc the ecc
+		yaffs_CalcTagsECC(tags);
+		
+		return 1; // recovered error
+	}
+	else if(ecc)
+	{
+		// Wierd ecc failure value
+		// TODO Need to do somethiong here
+		return -1; //unrecovered error
+	}
+	
+	return 0;
+}
+
+
+///////////////////////// TNODES ///////////////////////
+
+// List of spare tnodes
+// The list is hooked together using the first pointer
+// in the tnode.
+
+//static yaffs_Tnode *yaffs_freeTnodes = NULL;
+
+// static int yaffs_nFreeTnodes;
+
+//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL;
+
+
+
+// yaffs_CreateTnodes creates a bunch more tnodes and
+// adds them to the tnode free list.
+// Don't use this function directly
+
+static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)
+{
+    int i;
+    yaffs_Tnode *newTnodes;
+    yaffs_TnodeList *tnl;
+    
+    if(nTnodes < 1) return YAFFS_OK;
+   
+	// make these things
+	
+    newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode));
+   
+    if (!newTnodes)
+    {
+		T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not allocate Tnodes"TENDSTR)));
+		return YAFFS_FAIL;
+    }
+    
+    // Hook them into the free list
+    for(i = 0; i < nTnodes - 1; i++)
+    {
+    	newTnodes[i].internal[0] = &newTnodes[i+1];
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+    	newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
+    }
+    	
+	newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+   	newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
+	dev->freeTnodes = newTnodes;
+	dev->nFreeTnodes+= nTnodes;
+	dev->nTnodesCreated += nTnodes;
+
+	// Now add this bunch of tnodes to a list for freeing up.
+	// NB If we can't add this to the management list it isn't fatal
+	// but it just means we can't free this bunch of tnodes later.
+	tnl = YMALLOC(sizeof(yaffs_TnodeList));
+	if(!tnl)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not add tnodes to management list" TENDSTR)));
+		
+	}
+	else
+	{
+		tnl->tnodes = newTnodes;
+		tnl->next = dev->allocatedTnodeList;
+		dev->allocatedTnodeList = tnl;
+	}
+
+
+	T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Tnodes added" TENDSTR)));
+
+
+	return YAFFS_OK;
+}
+
+
+// GetTnode gets us a clean tnode. Tries to make allocate more if we run out
+static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
+{
+	yaffs_Tnode *tn = NULL;
+	
+	// If there are none left make more
+	if(!dev->freeTnodes)
+	{
+		yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES);
+	}
+	
+	if(dev->freeTnodes)
+	{
+		tn = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+    	if(tn->internal[YAFFS_NTNODES_INTERNAL] != 1)
+		{
+			// Hoosterman, this thing looks like it isn't in the list
+				T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 1" TENDSTR)));
+		}
+#endif
+		dev->freeTnodes = dev->freeTnodes->internal[0];
+		dev->nFreeTnodes--;
+		// zero out
+		memset(tn,0,sizeof(yaffs_Tnode));
+	}
+	
+
+	return tn;
+}
+
+
+// FreeTnode frees up a tnode and puts it back on the free list
+static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)
+{
+	if(tn)
+	{
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+    	if(tn->internal[YAFFS_NTNODES_INTERNAL] != 0)
+		{
+			// Hoosterman, this thing looks like it is already in the list
+				T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 2" TENDSTR)));
+		}
+		tn->internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
+		tn->internal[0] = dev->freeTnodes;
+		dev->freeTnodes = tn;
+		dev->nFreeTnodes++;
+	}
+}
+
+
+static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)
+{
+	// Free the list of allocated tnodes
+	yaffs_TnodeList *tmp;
+		
+	while(dev->allocatedTnodeList)
+	{
+		tmp =  dev->allocatedTnodeList->next;
+
+		YFREE(dev->allocatedTnodeList->tnodes);
+		YFREE(dev->allocatedTnodeList);
+		dev->allocatedTnodeList	= tmp;
+		
+	}
+	
+	dev->freeTnodes = NULL;
+	dev->nFreeTnodes = 0;
+}
+
+static void yaffs_InitialiseTnodes(yaffs_Device*dev)
+{
+	dev->allocatedTnodeList = NULL;
+	dev->freeTnodes = NULL;
+	dev->nFreeTnodes = 0;
+	dev->nTnodesCreated = 0;
+
+}
+
+#if 0
+void yaffs_TnodeTest(yaffs_Device *dev)
+{
+
+	int i;
+	int j;
+	yaffs_Tnode *tn[1000];
+	
+	YINFO("Testing TNodes");
+	
+	for(j = 0; j < 50; j++)
+	{
+		for(i = 0; i < 1000; i++)
+		{
+			tn[i] = yaffs_GetTnode(dev);
+			if(!tn[i])
+			{
+				YALERT("Getting tnode failed");
+			}
+		}
+		for(i = 0; i < 1000; i+=3)
+		{
+			yaffs_FreeTnode(dev,tn[i]);
+			tn[i] = NULL;
+		}
+		
+	}
+}
+#endif
+
+
+////////////////// END OF TNODE MANIPULATION ///////////////////////////
+
+/////////////// Functions to manipulate the look-up tree (made up of tnodes)
+// The look up tree is represented by the top tnode and the number of topLevel
+// in the tree. 0 means only the level 0 tnode is in the tree.
+
+
+// FindLevel0Tnode finds the level 0 tnode, if one exists.
+// Used when reading.....
+static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId)
+{
+	
+	yaffs_Tnode *tn = fStruct->top;
+	__u32 i;
+	int requiredTallness;	
+	int level = fStruct->topLevel;
+	
+	// Check sane level and chunk Id
+	if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
+	{
+//		char str[50];
+//		sprintf(str,"Bad level %d",level);
+//		YALERT(str);
+		return NULL;
+	}
+	
+	if(chunkId > YAFFS_MAX_CHUNK_ID)
+	{
+//		char str[50];
+//		sprintf(str,"Bad chunkId %d",chunkId);
+//		YALERT(str);
+		return NULL;
+	}
+
+	// First check we're tall enough (ie enough topLevel)
+	
+	i = chunkId >> (/*dev->chunkGroupBits  + */YAFFS_TNODES_LEVEL0_BITS);
+	requiredTallness = 0;
+	while(i)
+	{
+		i >>= YAFFS_TNODES_INTERNAL_BITS;
+		requiredTallness++;
+	}
+	
+	
+	if(requiredTallness > fStruct->topLevel)
+	{
+		// Not tall enough, so we can't find it, return NULL.
+		return NULL;
+	}
+		
+	
+	// Traverse down to level 0
+	while (level > 0 && tn)
+	{
+	    tn = tn->internal[(chunkId >>(/* dev->chunkGroupBits + */ YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) & 
+	                       YAFFS_TNODES_INTERNAL_MASK]; 
+		level--;
+	
+	}
+	
+	return tn;		
+}
+
+// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
+// This happens in two steps:
+//  1. If the tree isn't tall enough, then make it taller.
+//  2. Scan down the tree towards the level 0 tnode adding tnodes if required.
+//
+// Used when modifying the tree.
+//
+static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId)
+{
+	
+	yaffs_Tnode *tn; 
+	
+	int requiredTallness;
+	int i;
+	int l;
+	
+	__u32 x;
+		
+	
+	//T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId));
+	
+	// Check sane level and page Id
+	if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)
+	{
+//		char str[50];
+//		sprintf(str,"Bad level %d",fStruct->topLevel);
+//		YALERT(str);
+		return NULL;
+	}
+	
+	if(chunkId > YAFFS_MAX_CHUNK_ID)
+	{
+//		char str[50];
+//		sprintf(str,"Bad chunkId %d",chunkId);
+//		YALERT(str);
+		return NULL;
+	}
+	
+	// First check we're tall enough (ie enough topLevel)
+	
+	x = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);
+	requiredTallness = 0;
+	while(x)
+	{
+		x >>= YAFFS_TNODES_INTERNAL_BITS;
+		requiredTallness++;
+	}
+	
+	//T((TSTR(" required=%d"),requiredTallness));
+	
+	
+	if(requiredTallness > fStruct->topLevel)
+	{
+		// Not tall enough,gotta make the tree taller
+		for(i = fStruct->topLevel; i < requiredTallness; i++)
+		{
+			//T((TSTR(" add new top")));
+			
+			tn = yaffs_GetTnode(dev);
+			
+			if(tn)
+			{
+				tn->internal[0] = fStruct->top;
+				fStruct->top = tn;
+			}
+			else
+			{
+					T(YAFFS_TRACE_ERROR,(TSTR("yaffs: no more tnodes" TENDSTR)));
+			}
+		}
+		
+		fStruct->topLevel = requiredTallness;
+	}
+	
+	
+	// Traverse down to level 0, adding anything we need
+	
+	l = fStruct->topLevel;
+	tn = fStruct->top;
+	while (l > 0 && tn)
+	{
+		x = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & 
+	                       YAFFS_TNODES_INTERNAL_MASK;
+			       
+		//T((TSTR(" [%d:%d]"),l,i));
+		
+	    if(!tn->internal[x])
+	    {
+	    	//T((TSTR(" added")));
+		
+	    	tn->internal[x] = yaffs_GetTnode(dev);
+	    }
+	    
+	    tn = 	tn->internal[x];
+		l--;
+	
+	}
+	
+	//TSTR(TENDSTR)));
+	
+	return tn;		
+}
+
+// DeleteWorker scans backwards through the tnode tree and deletes all the
+// chunks and tnodes in the file
+// Returns 1 if the tree was deleted. Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
+
+static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit)
+{
+	int i;
+	int chunkInInode;
+	int theChunk;
+	yaffs_Tags tags;
+	int found;
+	int chunkDeleted;
+	int allDone = 1;
+	
+	
+	if(tn)
+	{
+		if(level > 0)
+		{
+		
+			for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
+			{
+			    if(tn->internal[i])
+		    	{
+					if(limit && (*limit) < 0)
+					{
+						allDone = 0;
+					}
+					else
+					{
+						allDone = yaffs_DeleteWorker(in,tn->internal[i],level - 1,
+										(chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ,limit);
+					}
+					if(allDone)
+					{
+						yaffs_FreeTnode(in->myDev,tn->internal[i]);
+			    		tn->internal[i] = NULL;
+					}
+			    }
+		    
+			}
+			return (allDone) ? 1 : 0;
+		}
+		else if(level == 0)
+		{
+			int hitLimit = 0;
+			
+			for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0 && !hitLimit; i--)
+			{
+			    if(tn->level0[i])
+		    	{
+					int j;
+					
+					chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i;
+					
+					theChunk =  tn->level0[i] << in->myDev->chunkGroupBits;
+
+					// Now we need to search for it
+					for(j = 0,found = 0; theChunk && j < in->myDev->chunkGroupSize && !found; j++)
+					{
+						yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,&tags,&chunkDeleted);
+						if(yaffs_TagsMatch(&tags,in->objectId,chunkInInode,chunkDeleted))
+						{
+							// found it;
+							found = 1;
+					
+						}
+						else
+						{
+							theChunk++;
+						}
+					}
+					
+					if(found)
+					{
+						yaffs_DeleteChunk(in->myDev,theChunk,1);
+						in->nDataChunks--;
+						if(limit)
+						{ 
+							*limit = *limit-1;
+							if(*limit <= 0) 
+							{ 
+								hitLimit = 1;
+							}
+						}
+					
+					}
+					
+			    	tn->level0[i] = 0;
+			    }
+		    
+			}
+			return (i < 0) ? 1 : 0;
+
+			
+		}
+		
+	}
+	
+	return 1;
+	
+}
+
+// SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
+// All soft deleting does is increment the block's softdelete count and pulls the chunk out
+// of the tnode.
+// THus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
+//
+static int yaffs_SoftDeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)
+{
+	int i;
+	int chunkInInode;
+	int theChunk;
+	yaffs_BlockInfo *theBlock;
+	yaffs_Tags tags;
+	int found;
+	int chunkDeleted;
+	int allDone = 1;
+	
+	
+	if(tn)
+	{
+		if(level > 0)
+		{
+		
+			for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
+			{
+			    if(tn->internal[i])
+		    	{
+						allDone = yaffs_SoftDeleteWorker(in,tn->internal[i],level - 1,
+										(chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i);
+					if(allDone)
+					{
+						yaffs_FreeTnode(in->myDev,tn->internal[i]);
+			    		tn->internal[i] = NULL;
+					}
+					else
+					{
+						//Hoosterman... how could this happen.
+					}			    
+				}		    
+			}
+			return (allDone) ? 1 : 0;
+		}
+		else if(level == 0)
+		{
+			
+			for(i = YAFFS_NTNODES_LEVEL0 -1; i >=0; i--)
+			{
+			    if(tn->level0[i])
+		    	{
+					// Note this does not find the real chunk, only the chunk group.
+					// We make an assumption that a chunk group is niot larger than a block.
+					theChunk =  (tn->level0[i] << in->myDev->chunkGroupBits);
+					T(YAFFS_TRACE_SCAN,(TSTR("soft delete tch %d cgb %d chunk %d" TENDSTR),
+						tn->level0[i],in->myDev->chunkGroupBits,theChunk));
+						
+					theBlock =	yaffs_GetBlockInfo(in->myDev,  theChunk/in->myDev->nChunksPerBlock);
+					if(theBlock)
+					{
+						theBlock->softDeletions++;
+					}
+			    	tn->level0[i] = 0;
+			    }
+		    
+			}
+			return 1;
+			
+		}
+		
+	}
+	
+	return 1;
+		
+}
+
+
+
+static void yaffs_SoftDeleteFile(yaffs_Object *obj)
+{
+	if(obj->deleted &&
+	   obj->variantType == YAFFS_OBJECT_TYPE_FILE &&
+	   !obj->softDeleted)
+	{
+		if(obj->nDataChunks <= 0)
+		{
+				// Empty file, just delete it immediately
+				yaffs_FreeTnode(obj->myDev,obj->variant.fileVariant.top);
+				obj->variant.fileVariant.top = NULL;
+				T(YAFFS_TRACE_TRACING,(TSTR("yaffs: Deleting empty file %d" TENDSTR),obj->objectId));
+				yaffs_DoGenericObjectDeletion(obj);	
+		}
+		else
+		{
+			yaffs_SoftDeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0);
+			obj->softDeleted = 1;
+		}
+	}
+}
+
+
+
+
+
+// Pruning removes any part of the file structure tree that is beyond the
+// bounds of the file (ie that does not point to chunks).
+//
+// A file should only get pruned when its size is reduced.
+//
+// Before pruning, the chunks must be pulled from the tree and the
+// level 0 tnode entries must be zeroed out.
+// Could also use this for file deletion, but that's probably better handled
+// by a special case.
+
+// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure()
+
+static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0)
+{
+	int i;
+	int hasData;
+	
+	if(tn)
+	{
+		hasData = 0;
+		
+		for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++)
+		{
+		    if(tn->internal[i] && level > 0)
+		    {
+		    	tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1);
+		    }
+		    
+		    if(tn->internal[i])
+		    {
+		    	hasData++;
+			}
+		}
+		
+		if(hasData == 0 && del0)
+		{
+			// Free and return NULL
+			
+			yaffs_FreeTnode(dev,tn);
+			tn = NULL;
+		}
+		
+	}
+
+	return tn;
+	
+}
+
+static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct)
+{
+	int i;
+	int hasData;
+	int done = 0;
+	yaffs_Tnode *tn;
+	
+	if(fStruct->topLevel > 0)
+	{
+		fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0);
+		
+		// Now we have a tree with all the non-zero branches NULL but the height
+		// is the same as it was.
+		// Let's see if we can trim internal tnodes to shorten the tree.
+		// We can do this if only the 0th element in the tnode is in use 
+		// (ie all the non-zero are NULL)
+		
+		while(fStruct->topLevel && !done)
+		{
+			tn = fStruct->top;
+			
+			hasData = 0;
+			for(i = 1; i <YAFFS_NTNODES_INTERNAL; i++)
+			{
+				if(tn->internal[i])
+		    	{
+		    		hasData++;
+				}
+			}
+			
+			if(!hasData)
+			{
+				fStruct->top = tn->internal[0];
+				fStruct->topLevel--;
+				yaffs_FreeTnode(dev,tn);
+			}
+			else
+			{
+				done = 1;
+			}
+		}
+	}
+	
+	return YAFFS_OK;
+}
+
+
+
+
+
+/////////////////////// End of File Structure functions. /////////////////
+
+// yaffs_CreateFreeObjects creates a bunch more objects and
+// adds them to the object free list.
+static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
+{
+    int i;
+    yaffs_Object *newObjects;
+    yaffs_ObjectList *list;
+    
+    if(nObjects < 1) return YAFFS_OK;
+   
+	// make these things
+	
+    newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
+   
+    if (!newObjects)
+    {
+		T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Could not allocate more objects" TENDSTR)));
+		return YAFFS_FAIL;
+    }
+    
+    // Hook them into the free list
+    for(i = 0; i < nObjects - 1; i++)
+    {
+    	newObjects[i].siblings.next = (struct list_head *)(&newObjects[i+1]);
+    }
+    	
+	newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
+	dev->freeObjects = newObjects;
+	dev->nFreeObjects+= nObjects;
+	dev->nObjectsCreated+= nObjects;
+	
+	// Now add this bunch of Objects to a list for freeing up.
+	
+	list = YMALLOC(sizeof(yaffs_ObjectList));
+	if(!list)
+	{
+		T(YAFFS_TRACE_ALLOCATE,(TSTR("Could not add objects to management list" TENDSTR)));
+	}
+	else
+	{
+		list->objects = newObjects;
+		list->next = dev->allocatedObjectList;
+		dev->allocatedObjectList = list;
+	}
+	
+	
+	
+	return YAFFS_OK;
+}
+
+
+// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out
+static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
+{
+	yaffs_Object *tn = NULL;
+	
+	// If there are none left make more
+	if(!dev->freeObjects)
+	{
+		yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS);
+	}
+	
+	if(dev->freeObjects)
+	{
+		tn = dev->freeObjects;
+		dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next);
+		dev->nFreeObjects--;
+		
+		// Now sweeten it up...
+	
+		memset(tn,0,sizeof(yaffs_Object));
+		tn->myDev = dev;
+		tn->chunkId = -1;
+		tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
+		INIT_LIST_HEAD(&(tn->hardLinks));
+		INIT_LIST_HEAD(&(tn->hashLink));
+		INIT_LIST_HEAD(&tn->siblings);
+		
+		// Add it to the lost and found directory.
+		// NB Can't put root or lostNFound in lostNFound so
+		// check if lostNFound exists first
+		if(dev->lostNFoundDir)
+		{
+			yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn);	
+		}
+	}
+	
+
+	return tn;
+}
+
+static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode)
+{
+
+	yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY);		
+	if(obj)
+	{
+		obj->fake = 1;			// it is fake so it has no NAND presence...
+		obj->renameAllowed= 0;	// ... and we're not allowed to rename it...
+		obj->unlinkAllowed= 0;	// ... or unlink it
+		obj->deleted = 0;
+		obj->unlinked = 0;
+		obj->yst_mode = mode;
+		obj->myDev = dev;
+		obj->chunkId = 0; // Not a valid chunk.
+	}
+	
+	return obj;
+	
+}
+
+
+static void yaffs_UnhashObject(yaffs_Object *tn)
+{
+	int bucket;
+	yaffs_Device *dev = tn->myDev;
+	
+	
+	// If it is still linked into the bucket list, free from the list
+	if(!list_empty(&tn->hashLink))
+	{
+		list_del_init(&tn->hashLink);
+		bucket =  yaffs_HashFunction(tn->objectId);
+		dev->objectBucket[bucket].count--;
+	}
+	
+}
+
+
+// FreeObject frees up a Object and puts it back on the free list
+static void yaffs_FreeObject(yaffs_Object *tn)
+{
+
+	yaffs_Device *dev = tn->myDev;
+	
+#ifdef  __KERNEL__
+	if(tn->myInode)
+	{
+		// We're still hooked up to a cached inode.
+		// Don't delete now, but mark for later deletion
+		tn->deferedFree = 1;
+		return;
+	}
+#endif
+	
+	yaffs_UnhashObject(tn);
+	
+	// Link into the free list.
+	tn->siblings.next = (struct list_head *)(dev->freeObjects);
+	dev->freeObjects = tn;
+	dev->nFreeObjects++;
+}
+
+
+#ifdef __KERNEL__
+
+void yaffs_HandleDeferedFree(yaffs_Object *obj)
+{
+	if(obj->deferedFree)
+	{
+	   yaffs_FreeObject(obj);
+	}
+}
+
+#endif
+
+
+
+static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
+{
+	// Free the list of allocated Objects
+	
+	yaffs_ObjectList *tmp;
+	
+	while( dev->allocatedObjectList)
+	{
+		tmp =  dev->allocatedObjectList->next;
+		YFREE(dev->allocatedObjectList->objects);
+		YFREE(dev->allocatedObjectList);
+		
+		dev->allocatedObjectList =  tmp;
+	}
+	
+	dev->freeObjects = NULL;
+	dev->nFreeObjects = 0;
+}
+
+static void yaffs_InitialiseObjects(yaffs_Device *dev)
+{
+	int i;
+	
+	dev->allocatedObjectList = NULL;
+	dev->freeObjects = NULL;
+	dev->nFreeObjects = 0;
+	
+	for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++)
+	{
+		INIT_LIST_HEAD(&dev->objectBucket[i].list);
+		dev->objectBucket[i].count = 0;	
+	}
+
+}
+
+
+
+
+
+
+int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
+{
+	static int x = 0;
+	int i;
+	int l = 999;
+	int lowest = 999999;
+
+		
+	// First let's see if we can find one that's empty.
+	
+	for(i = 0; i < 10 && lowest > 0; i++)
+	 {
+		x++;
+		x %=  YAFFS_NOBJECT_BUCKETS;
+		if(dev->objectBucket[x].count < lowest)
+		{
+			lowest = dev->objectBucket[x].count;
+			l = x;
+		}
+		
+	}
+	
+	// If we didn't find an empty list, then try
+	// looking a bit further for a short one
+	
+	for(i = 0; i < 10 && lowest > 3; i++)
+	 {
+		x++;
+		x %=  YAFFS_NOBJECT_BUCKETS;
+		if(dev->objectBucket[x].count < lowest)
+		{
+			lowest = dev->objectBucket[x].count;
+			l = x;
+		}
+		
+	}
+	
+	return l;
+}
+
+static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)
+{
+	int bucket = yaffs_FindNiceObjectBucket(dev);
+	
+	// Now find an object value that has not already been taken
+	// by scanning the list.
+	
+	int found = 0;
+	struct list_head *i;
+	
+	__u32 n = (__u32)bucket;
+
+	//yaffs_CheckObjectHashSanity();	
+	
+	while(!found)
+	{
+		found = 1;
+		n +=  YAFFS_NOBJECT_BUCKETS;
+		if(1 ||dev->objectBucket[bucket].count > 0)
+		{
+			list_for_each(i,&dev->objectBucket[bucket].list)
+			{
+				// If there is already one in the list
+				if(i && list_entry(i, yaffs_Object,hashLink)->objectId == n)
+				{
+					found = 0;
+				}
+			}
+		}
+	}
+	
+	//T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n);
+	
+	return n;	
+}
+
+void yaffs_HashObject(yaffs_Object *in)
+{
+	int bucket = yaffs_HashFunction(in->objectId);
+	yaffs_Device *dev = in->myDev;
+	
+	if(!list_empty(&in->hashLink))
+	{
+		//YINFO("!!!");
+	}
+
+	
+	list_add(&in->hashLink,&dev->objectBucket[bucket].list);
+	dev->objectBucket[bucket].count++;
+
+}
+
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number)
+{
+	int bucket = yaffs_HashFunction(number);
+	struct list_head *i;
+	yaffs_Object *in;
+	
+	list_for_each(i,&dev->objectBucket[bucket].list)
+	{
+		// Look if it is in the list
+		if(i)
+		{
+			in = list_entry(i, yaffs_Object,hashLink);
+			if(in->objectId == number)
+			{
+#ifdef __KERNEL__
+				// Don't tell the VFS about this if it has been marked for freeing
+ 				if(in->deferedFree) 
+				    return NULL;
+#endif
+				return in;
+			}
+		}
+	}
+	
+	return NULL;
+}
+
+
+
+yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type)
+{
+		
+	yaffs_Object *theObject;
+
+	if(number < 0)
+	{
+		number = yaffs_CreateNewObjectNumber(dev);
+	}
+	
+	theObject = yaffs_AllocateEmptyObject(dev);
+	
+	if(theObject)
+	{
+		theObject->fake = 0;
+		theObject->renameAllowed = 1;
+		theObject->unlinkAllowed = 1;
+		theObject->objectId = number;
+		yaffs_HashObject(theObject);
+		theObject->variantType = type;
+#ifdef CONFIG_YAFFS_WINCE
+		yfsd_WinFileTimeNow(theObject->win_atime);
+		theObject->win_ctime[0] = theObject->win_mtime[0] = theObject->win_atime[0];
+		theObject->win_ctime[1] = theObject->win_mtime[1] = theObject->win_atime[1];
+
+#else
+
+		theObject->yst_atime = theObject->yst_mtime =	theObject->yst_ctime = Y_CURRENT_TIME;
+
+#endif
+		switch(type)
+		{
+			case YAFFS_OBJECT_TYPE_FILE: 
+				theObject->variant.fileVariant.fileSize = 0;
+				theObject->variant.fileVariant.scannedFileSize = 0;
+				theObject->variant.fileVariant.topLevel = 0;
+				theObject->variant.fileVariant.top  = yaffs_GetTnode(dev);
+				break;
+			case YAFFS_OBJECT_TYPE_DIRECTORY:
+				INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);
+				break;
+			case YAFFS_OBJECT_TYPE_SYMLINK:
+				// No action required
+				break;
+			case YAFFS_OBJECT_TYPE_HARDLINK:
+				// No action required
+				break;
+			case YAFFS_OBJECT_TYPE_SPECIAL:
+				// No action required
+				break;
+			case YAFFS_OBJECT_TYPE_UNKNOWN:
+				// todo this should not happen
+				break;
+		}
+	}
+	
+	return theObject;
+}
+
+yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)
+{
+	yaffs_Object *theObject = NULL;
+	
+	if(number > 0)
+	{
+		theObject = yaffs_FindObjectByNumber(dev,number);
+	}
+	
+	if(!theObject)
+	{
+		theObject = yaffs_CreateNewObject(dev,number,type);
+	}
+	
+	return theObject;
+
+}
+
+char *yaffs_CloneString(const char *str)
+{
+	char *newStr = NULL;
+	
+	if(str && *str)
+	{
+		newStr = YMALLOC(strlen(str) + 1);
+		strcpy(newStr,str);
+	}
+
+	return newStr;
+	
+}
+
+//
+// Mknod (create) a new object.
+// equivalentObject only has meaning for a hard link;
+// aliasString only has meaning for a sumlink.
+// rdev only has meaning for devices (a subset of special objects)
+yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
+								 yaffs_Object *parent,
+								 const char *name, 
+								 __u32 mode,
+								 __u32 uid,
+								 __u32 gid,
+								 yaffs_Object *equivalentObject,
+								 const char *aliasString,
+								 __u32 rdev)
+{
+	yaffs_Object *in;
+
+	yaffs_Device *dev = parent->myDev;
+	
+	// Check if the entry exists. If it does then fail the call since we don't want a dup.
+	if(yaffs_FindObjectByName(parent,name))
+	{
+		return NULL;
+	}
+	
+	in = yaffs_CreateNewObject(dev,-1,type);
+	
+	if(in)
+	{
+		in->chunkId = -1;
+		in->valid = 1;
+		in->variantType = type;
+
+		in->yst_mode  = mode;
+		
+#ifdef CONFIG_YAFFS_WINCE
+		yfsd_WinFileTimeNow(in->win_atime);
+		in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
+		in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
+		
+#else
+
+		in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
+		in->yst_rdev  = rdev;
+		in->yst_uid   = uid;
+		in->yst_gid   = gid;
+#endif		
+		in->nDataChunks = 0;
+
+		yaffs_SetObjectName(in,name);
+		in->dirty = 1;
+		
+		yaffs_AddObjectToDirectory(parent,in);
+		
+		in->myDev = parent->myDev;
+		
+				
+		switch(type)
+		{
+			case YAFFS_OBJECT_TYPE_SYMLINK:
+				in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString);
+				break;
+			case YAFFS_OBJECT_TYPE_HARDLINK:
+				in->variant.hardLinkVariant.equivalentObject = equivalentObject;
+				in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId;
+				list_add(&in->hardLinks,&equivalentObject->hardLinks);
+				break;
+			case YAFFS_OBJECT_TYPE_FILE: // do nothing
+			case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing
+			case YAFFS_OBJECT_TYPE_SPECIAL: // do nothing
+			case YAFFS_OBJECT_TYPE_UNKNOWN:
+				break;
+		}
+
+		if(/*yaffs_GetNumberOfFreeChunks(dev) <= 0 || */
+		   yaffs_UpdateObjectHeader(in,name,0) < 0)
+		{
+			// Could not create the object header, fail the creation
+			yaffs_AbortHalfCreatedObject(in);
+			in = NULL;
+		}
+
+	}
+	
+	return in;
+}
+
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL,0);
+}
+
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,0);
+}
+
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL,parent,name,mode,uid,gid,NULL,NULL,rdev);
+}
+
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias,0);
+}
+
+// NB yaffs_Link returns the object id of the equivalent object.
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject)
+{
+	// Get the real object in case we were fed a hard link as an equivalent object
+	equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
+	
+	if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL,0))
+	{
+		return equivalentObject;
+	}
+	else
+	{
+		return NULL;
+	}
+	
+}
+
+
+static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName,int force)
+{
+	int unlinkOp;
+
+	if(newDir == NULL)
+	{
+		newDir = obj->parent; // use the old directory
+	}
+
+	unlinkOp = (newDir == obj->myDev->unlinkedDir && obj->variantType == YAFFS_OBJECT_TYPE_FILE);
+	
+	// If the object is a file going into the unlinked directory, then it is OK to just stuff it in since
+	// duplicate names are allowed.
+	// Otherwise only proceed if the new name does not exist and if we're putting it into a directory.
+	if( (unlinkOp|| 
+		 force || 
+		 !yaffs_FindObjectByName(newDir,newName))  &&
+	     newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
+	{
+		yaffs_SetObjectName(obj,newName);
+		obj->dirty = 1;
+		
+		yaffs_AddObjectToDirectory(newDir,obj);
+		
+		if(unlinkOp) obj->unlinked = 1;
+		
+		
+		if(yaffs_UpdateObjectHeader(obj,newName,0) >= 0)
+		{
+			return YAFFS_OK;
+		}
+	}
+	
+	return YAFFS_FAIL;
+}
+
+
+
+int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)
+{
+	yaffs_Object *obj;
+	int force = 0;
+	
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+	// Special case for WinCE.
+	// While look-up is case insensitive, the name isn't.
+	// THerefore we might want to change x.txt to X.txt
+	if(oldDir == newDir && _stricmp(oldName,newName) == 0)
+	{
+		force = 1;
+	}	
+#endif
+	
+	obj = yaffs_FindObjectByName(oldDir,oldName);
+	if(obj && obj->renameAllowed)
+	{
+		return yaffs_ChangeObjectName(obj,newDir,newName,force);
+	}
+	return YAFFS_FAIL;
+}
+
+
+
+static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)
+{
+	// Scan the buckets and check that the lists 
+	// have as many members as the count says there are
+	int bucket;
+	int countEm;
+	struct list_head *j;
+	int ok = YAFFS_OK;
+	
+	for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++)
+	{
+		countEm = 0;
+		
+		list_for_each(j,&dev->objectBucket[bucket].list)
+		{
+			countEm++;
+		}
+		
+		if(countEm != dev->objectBucket[bucket].count)
+		{
+			T(YAFFS_TRACE_ERROR,(TSTR("Inode hash inconsistency" TENDSTR)));
+			ok = YAFFS_FAIL;
+		}
+	}
+
+	return ok;
+}
+
+#if 0
+void yaffs_ObjectTest(yaffs_Device *dev)
+{
+	yaffs_Object *in[1000];
+	int inNo[1000];
+	yaffs_Object *inold[1000];
+	int i;
+	int j;
+	
+	memset(in,0,1000*sizeof(yaffs_Object *));
+	memset(inold,0,1000*sizeof(yaffs_Object *));
+	
+	yaffs_CheckObjectHashSanity(dev);
+	
+	for(j = 0; j < 10; j++)
+	{
+		//T(("%d\n",j));
+		
+		for(i = 0; i < 1000; i++)
+		{
+			in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE);
+			if(!in[i])
+			{
+				YINFO("No more inodes");
+			}
+			else
+			{
+				inNo[i] = in[i]->objectId;
+			}
+		}
+		
+		for(i = 0; i < 1000; i++)
+		{
+			if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i])
+			{
+				//T(("Differnce in look up test\n"));
+			}
+			else
+			{
+				// T(("Look up ok\n"));
+			}
+		}
+		
+		yaffs_CheckObjectHashSanity(dev);
+	
+		for(i = 0; i < 1000; i+=3)
+		{
+			yaffs_FreeObject(in[i]);	
+			in[i] = NULL;
+		}
+		
+	
+		yaffs_CheckObjectHashSanity(dev);
+	}
+		
+}
+
+#endif
+
+/////////////////////////// Block Management and Page Allocation ///////////////////
+
+
+static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks)
+{
+	dev->allocationBlock = -1; // force it to get a new one
+	//Todo we're assuming the malloc will pass.
+	dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
+	// Set up dynamic blockinfo stuff.
+	dev->chunkBitmapStride = (dev->nChunksPerBlock+7)/8;
+	dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
+	if(dev->blockInfo && dev->chunkBits)
+	{
+		memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo));
+		memset(dev->chunkBits,0,dev->chunkBitmapStride * nBlocks);
+		return YAFFS_OK;
+	}
+	
+	return YAFFS_FAIL;
+	
+}
+
+static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
+{
+	YFREE(dev->blockInfo);
+	dev->blockInfo = NULL;
+	YFREE(dev->chunkBits);
+	dev->chunkBits = NULL;
+}
+
+// FindDiretiestBlock is used to select the dirtiest block (or close enough)
+// for garbage collection.
+
+static int yaffs_FindDirtiestBlock(yaffs_Device *dev,int aggressive)
+{
+
+	int b = dev->currentDirtyChecker;
+	
+	int i;
+	int iterations;
+	int dirtiest = -1;
+	int pagesInUse; 
+	yaffs_BlockInfo *bi;
+
+	// If we're doing aggressive GC then we are happy to take a less-dirty block, and
+	// search further.
+	
+	pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
+	if(aggressive)
+	{
+		iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
+	}
+	else
+	{
+		iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
+		iterations = iterations / 16;
+		if(iterations > 200)
+		{
+			iterations = 200;
+		}
+	}
+	
+	for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
+	{
+		b++;
+		if ( b < dev->internalStartBlock || b > dev->internalEndBlock)
+		{
+			b =  dev->internalStartBlock;
+		}
+
+		if(b < dev->internalStartBlock || b > dev->internalEndBlock)
+		{
+			T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));
+			YBUG();
+		}
+		
+		bi = yaffs_GetBlockInfo(dev,b);
+		
+		if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+		   (bi->pagesInUse - bi->softDeletions )< pagesInUse)
+		{
+			dirtiest = b;
+			pagesInUse = (bi->pagesInUse - bi->softDeletions);
+		}
+	}
+	
+	dev->currentDirtyChecker = b;
+	
+	return dirtiest;
+}
+
+
+static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
+{
+	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,blockNo);
+	
+	int erasedOk = 0;
+	
+	// If the block is still healthy erase it and mark as clean.
+	// If the block has had a data failure, then retire it.
+	bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
+
+	if(!bi->needsRetiring)
+	{
+		erasedOk = yaffs_EraseBlockInNAND(dev,blockNo);
+		if(!erasedOk)
+		{
+			T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Erasure failed %d" TENDSTR),blockNo));
+		}
+	}
+	
+	if( erasedOk )
+	{
+		// Clean it up...
+		bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
+		dev->nErasedBlocks++;
+		bi->pagesInUse = 0;
+		bi->softDeletions = 0;
+		yaffs_ClearChunkBits(dev,blockNo);
+	
+		T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));
+	}
+	else
+	{
+		yaffs_RetireBlock(dev,blockNo);
+		T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" TENDSTR),blockNo));
+	}
+}
+
+
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
+{
+	int i;
+	
+	yaffs_BlockInfo *bi;
+	
+	if(dev->nErasedBlocks < 1)
+	{
+		// Hoosterman we've got a problem.
+		// Can't get space to gc
+		T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during gc" TENDSTR)));
+
+		return -1;
+	}
+	
+	// Find an empty block.
+	
+	for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++)
+	{
+		dev->allocationBlockFinder++;
+		if(dev->allocationBlockFinder <dev->internalStartBlock || dev->allocationBlockFinder> dev->internalEndBlock) 
+		{
+			dev->allocationBlockFinder = dev->internalStartBlock;
+		}
+		
+		bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
+
+		if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
+		{
+			bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
+			dev->nErasedBlocks--;			
+			return dev->allocationBlockFinder;
+		}
+	}
+	
+	return -1;	
+}
+
+
+
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
+{
+	int retVal;
+	yaffs_BlockInfo *bi;
+	
+	if(dev->allocationBlock < 0)
+	{
+		// Get next block to allocate off
+		dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
+		dev->allocationPage = 0;
+	}
+	
+	if(!useReserve &&  dev->nErasedBlocks <= dev->nReservedBlocks)
+	{
+		// Not enough space to allocate unless we're allowed to use the reserve.
+		return -1;
+	}
+	
+	// Next page please....
+	if(dev->allocationBlock >= 0)
+	{
+		bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);
+		
+		retVal = (dev->allocationBlock * dev->nChunksPerBlock) + 
+			  	  dev->allocationPage;
+		bi->pagesInUse++;
+		yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
+
+		dev->allocationPage++;
+		
+		dev->nFreeChunks--;
+		
+		// If the block is full set the state to full
+		if(dev->allocationPage >= dev->nChunksPerBlock)
+		{
+			bi->blockState = YAFFS_BLOCK_STATE_FULL;
+			dev->allocationBlock = -1;
+		}
+
+
+		return retVal;
+		
+	}
+	T(YAFFS_TRACE_ERROR,(TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
+
+	return -1;	
+}
+
+// To determine if we have enough space we just look at the 
+// number of erased blocks.
+// The cache is allowed to use reserved blocks.
+
+static int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)
+{
+	return (dev->nErasedBlocks >= dev->nReservedBlocks);
+}
+
+
+static int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
+{
+	int oldChunk;
+	int newChunk;
+	int chunkInBlock;
+	int markNAND;
+	
+	
+	yaffs_Spare spare;
+	yaffs_Tags  tags;
+	__u8  buffer[YAFFS_BYTES_PER_CHUNK];
+	
+//	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
+	
+	yaffs_Object *object;
+
+	//T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));	
+	
+	for(chunkInBlock = 0,oldChunk = block * dev->nChunksPerBlock; 
+	    chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
+	    chunkInBlock++, oldChunk++ )
+	{
+		if(yaffs_CheckChunkBit(dev,block,chunkInBlock))
+		{
+			
+			// This page is in use and might need to be copied off
+			
+			markNAND = 1;
+			
+			//T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
+			
+			yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare,1);
+			
+			yaffs_GetTagsFromSpare(dev,&spare,&tags);
+
+			object = yaffs_FindObjectByNumber(dev,tags.objectId);
+			
+			if(object && object->deleted && tags.chunkId != 0)
+			{
+				// Data chunk in a deleted file, throw it away
+				// It's a deleted data chunk,
+				// No need to copy this, just forget about it and fix up the
+				// object.
+				
+				//yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0); 
+				object->nDataChunks--;
+				
+				if(object->nDataChunks <= 0)
+				{
+					// Time to delete the file too
+					yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+					object->variant.fileVariant.top = NULL;
+					T(YAFFS_TRACE_TRACING,(TSTR("yaffs: About to finally delete object %d" TENDSTR),object->objectId));
+					yaffs_DoGenericObjectDeletion(object);					
+ 				}
+				markNAND = 0;
+			}
+			else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)
+			{
+				// Deleted object header with no data chunks.
+				// Can be discarded and the file deleted.
+				object->chunkId = 0;
+				yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+				object->variant.fileVariant.top = NULL;
+				yaffs_DoGenericObjectDeletion(object);
+				
+			}
+			else if(object)
+			{
+				// It's either a data chunk in a live file or
+				// an ObjectHeader, so we're interested in it.
+				// NB Need to keep the ObjectHeaders of deleted files
+				// until the whole file has been deleted off
+				tags.serialNumber++;
+				yaffs_LoadTagsIntoSpare(&spare,&tags);
+
+				dev->nGCCopies++;
+
+				newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
+			
+				if(newChunk < 0)
+				{
+					return YAFFS_FAIL;
+				}
+			
+				// Ok, now fix up the Tnodes etc.
+			
+				if(tags.chunkId == 0)
+				{
+					// It's a header
+					object->chunkId = newChunk;
+					object->serial = tags.serialNumber;
+				}
+				else
+				{
+					// It's a data chunk
+					yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
+				}
+			}
+			
+			yaffs_DeleteChunk(dev,oldChunk,markNAND);			
+			
+		}
+	}
+
+	return YAFFS_OK;
+}
+
+
+static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)
+{
+	// find a file to delete
+	struct list_head *i;	
+	yaffs_Object *l;
+
+
+	//Scan the unlinked files looking for one to delete
+	list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+	{
+		if(i)
+		{
+			l = list_entry(i, yaffs_Object,siblings);
+			if(l->deleted)
+			{
+				return l;			
+			}
+		}
+	}	
+	return NULL;
+}
+
+
+static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
+{
+	// This does background deletion on unlinked files.. only deleted ones.
+	// If we don't have a file we're working on then find one
+	if(!dev->unlinkedDeletion && dev->nDeletedFiles > 0)
+	{
+		dev->unlinkedDeletion = yaffs_FindDeletedUnlinkedFile(dev);
+	}
+	
+	// OK, we're working on a file...
+	if(dev->unlinkedDeletion)
+	{
+		yaffs_Object *obj = dev->unlinkedDeletion;
+		int delresult;
+		int limit; // Number of chunks to delete in a file.
+				   // NB this can be exceeded, but not by much.
+				   
+		limit = -1;
+
+		delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit);
+		
+		if(obj->nDataChunks == 0)
+		{
+			// Done all the deleting of data chunks.
+			// Now dump the header and clean up
+			yaffs_FreeTnode(dev,obj->variant.fileVariant.top);
+			obj->variant.fileVariant.top = NULL;
+			yaffs_DoGenericObjectDeletion(obj);
+			dev->nDeletedFiles--;
+			dev->nUnlinkedFiles--;
+			dev->nBackgroundDeletions++;
+			dev->unlinkedDeletion = NULL;	
+		}
+	}
+}
+
+
+#if 0
+#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
+{
+	int block;
+	int aggressive=0;
+	
+	//yaffs_DoUnlinkedFileDeletion(dev);
+	
+	if(dev->nErasedBlocks <= (dev->nReservedBlocks + YAFFS_GARBAGE_COLLECT_LOW_WATER))
+	{
+		aggressive = 1;
+	}		
+	
+	if(aggressive)
+	{
+		block = yaffs_FindDirtiestBlock(dev,aggressive);
+		
+		if(block >= 0)
+		{
+			dev->garbageCollections++;
+			return yaffs_GarbageCollectBlock(dev,block);
+		}	
+		else
+		{
+			return YAFFS_FAIL;
+		}
+	}
+
+	return YAFFS_OK;
+}
+#endif
+
+// New garbage collector
+// If we're very low on erased blocks then we do aggressive garbage collection
+// otherwise we do "passive" garbage collection.
+// Aggressive gc looks further (whole array) and will accept dirtier blocks.
+// Passive gc only inspects smaller areas and will only accept cleaner blocks.
+//
+// The idea is to help clear out space in a more spread-out manner.
+// Dunno if it really does anything useful.
+//
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
+{
+	int block;
+	int aggressive=0;
+	
+	//yaffs_DoUnlinkedFileDeletion(dev);
+	
+	if(dev->nErasedBlocks <= (dev->nReservedBlocks + 1))
+	{
+		aggressive = 1;
+	}		
+	
+	block = yaffs_FindDirtiestBlock(dev,aggressive);
+	
+	if(block >= 0)
+	{
+		dev->garbageCollections++;
+		if(!aggressive)
+		{
+			dev->passiveGarbageCollections++;
+		}
+
+		T(YAFFS_TRACE_GC,(TSTR("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),dev->nErasedBlocks,aggressive));
+
+		return yaffs_GarbageCollectBlock(dev,block);
+	}	
+
+	return aggressive ? YAFFS_FAIL : YAFFS_OK;
+}
+
+
+//////////////////////////// TAGS ///////////////////////////////////////
+
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)
+{
+	yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
+	
+	yaffs_CalcTagsECC(tagsPtr);
+	
+	sparePtr->tagByte0 = tu->asBytes[0];
+	sparePtr->tagByte1 = tu->asBytes[1];
+	sparePtr->tagByte2 = tu->asBytes[2];
+	sparePtr->tagByte3 = tu->asBytes[3];
+	sparePtr->tagByte4 = tu->asBytes[4];
+	sparePtr->tagByte5 = tu->asBytes[5];
+	sparePtr->tagByte6 = tu->asBytes[6];
+	sparePtr->tagByte7 = tu->asBytes[7];
+}
+
+static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
+{
+	yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
+	int result;
+
+	tu->asBytes[0]= sparePtr->tagByte0;
+	tu->asBytes[1]= sparePtr->tagByte1;
+	tu->asBytes[2]= sparePtr->tagByte2;
+	tu->asBytes[3]= sparePtr->tagByte3;
+	tu->asBytes[4]= sparePtr->tagByte4;
+	tu->asBytes[5]= sparePtr->tagByte5;
+	tu->asBytes[6]= sparePtr->tagByte6;
+	tu->asBytes[7]= sparePtr->tagByte7;
+	
+	result =  yaffs_CheckECCOnTags(tagsPtr);
+	if(result> 0)
+	{
+		dev->tagsEccFixed++;
+	}
+	else if(result <0)
+	{
+		dev->tagsEccUnfixed++;
+	}
+}
+
+static void yaffs_SpareInitialise(yaffs_Spare *spare)
+{
+	memset(spare,0xFF,sizeof(yaffs_Spare));
+}
+
+static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted)
+{
+	if(tags)
+	{
+		yaffs_Spare spare;
+		if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare,1) == YAFFS_OK)
+		{
+			*chunkDeleted = (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0;
+			yaffs_GetTagsFromSpare(dev,&spare,tags);
+			return YAFFS_OK;
+		}
+		else
+		{
+			return YAFFS_FAIL;
+		}
+	}
+	
+	return YAFFS_OK;
+}
+
+#if 0
+static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags)
+{
+	// NB There must be tags, data is optional
+	// If there is data, then an ECC is calculated on it.
+	
+	yaffs_Spare spare;
+	
+	if(!tags)
+	{
+		return YAFFS_FAIL;
+	}
+	
+	yaffs_SpareInitialise(&spare);
+	
+	if(!dev->useNANDECC && buffer)
+	{
+		yaffs_CalcECC(buffer,&spare);
+	}
+	
+	yaffs_LoadTagsIntoSpare(&spare,tags);
+	
+	return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare);
+	
+}
+#endif
+
+
+static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve)
+{
+	// NB There must be tags, data is optional
+	// If there is data, then an ECC is calculated on it.
+	
+	yaffs_Spare spare;
+	
+	if(!tags)
+	{
+		return YAFFS_FAIL;
+	}
+	
+	yaffs_SpareInitialise(&spare);
+	
+	if(!dev->useNANDECC && buffer)
+	{
+		yaffs_CalcECC(buffer,&spare);
+	}
+	
+	yaffs_LoadTagsIntoSpare(&spare,tags);
+	
+	return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);
+	
+}
+
+static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted)
+{
+	return 	(  tags->chunkId == chunkInObject &&
+			   tags->objectId == objectId &&
+			   !chunkDeleted) ? 1 : 0;
+	
+}
+
+
+
+int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
+{
+	//Get the Tnode, then get the level 0 offset chunk offset
+    yaffs_Tnode *tn;     
+    int theChunk = -1;
+    yaffs_Tags localTags;
+    int i;
+    int found = 0;
+    int chunkDeleted;
+    
+    yaffs_Device *dev = in->myDev;
+    
+    
+    if(!tags)
+    {
+    	// Passed a NULL, so use our own tags space
+    	tags = &localTags;
+    }
+    
+    tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+    
+    if(tn)
+    {
+		theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
+
+		// Now we need to do the shifting etc and search for it
+		for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
+		{
+			yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);
+			if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))
+			{
+				// found it;
+				found = 1;
+			}
+			else
+			{
+				theChunk++;
+			}
+		}
+    }
+    return found ? theChunk : -1;
+}
+
+int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
+{
+	//Get the Tnode, then get the level 0 offset chunk offset
+    yaffs_Tnode *tn;     
+    int theChunk = -1;
+    yaffs_Tags localTags;
+    int i;
+    int found = 0;
+    yaffs_Device *dev = in->myDev;
+    int chunkDeleted;
+    
+    if(!tags)
+    {
+    	// Passed a NULL, so use our own tags space
+    	tags = &localTags;
+    }
+    
+    tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+    
+    if(tn)
+    {
+    
+		theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
+    
+		// Now we need to do the shifting etc and search for it
+		for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
+		{
+			yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);
+			if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))
+			{
+				// found it;
+				found = 1;
+			}
+			else
+			{
+				theChunk++;
+			}
+		}
+    
+		// Delete the entry in the filestructure
+		if(found)
+		{
+			tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;
+		}
+    }
+    else
+    {
+    	//T(("No level 0 found for %d\n", chunkInInode));
+    }
+    
+    if(!found)
+    {
+    	//T(("Could not find %d to delete\n",chunkInInode));
+    }
+    return found ? theChunk : -1;
+}
+
+
+#ifdef YAFFS_PARANOID
+
+static int yaffs_CheckFileSanity(yaffs_Object *in)
+{
+	int chunk;
+	int nChunks;
+	int fSize;
+	int failed = 0;
+	int objId;
+	yaffs_Tnode *tn;
+    yaffs_Tags localTags;
+    yaffs_Tags *tags = &localTags;
+    int theChunk;
+    int chunkDeleted;
+    
+    	
+	if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
+	{
+		//T(("Object not a file\n"));
+		return YAFFS_FAIL;
+	}
+	
+	objId = in->objectId;
+	fSize  = in->variant.fileVariant.fileSize;
+	nChunks = (fSize + in->myDev->nBytesPerChunk -1)/in->myDev->nBytesPerChunk;
+	
+	for(chunk = 1; chunk <= nChunks; chunk++)
+	{
+		tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);
+    
+		if(tn)
+		{
+    
+			theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;
+    
+
+				yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags,&chunkDeleted);
+				if(yaffs_TagsMatch(tags,in->objectId,chunk,chunkDeleted))
+				{
+					// found it;
+				
+				}
+				else
+				{
+					//T(("File problem file [%d,%d] NAND %d  tags[%d,%d]\n",
+					//		objId,chunk,theChunk,tags->chunkId,tags->objectId);
+							
+					failed = 1;
+								
+				}
+    
+		}
+		else
+		{
+			//T(("No level 0 found for %d\n", chunk));
+		}
+	}
+	
+	return failed ? YAFFS_FAIL : YAFFS_OK;
+}
+
+#endif
+
+static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)
+{
+	yaffs_Tnode *tn;
+	yaffs_Device *dev = in->myDev;
+	int existingChunk;
+	yaffs_Tags existingTags;
+	yaffs_Tags newTags;
+	unsigned existingSerial, newSerial;
+	
+	int newChunkDeleted;
+	
+	
+	tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+	if(!tn)
+	{
+		return YAFFS_FAIL;
+	}
+
+	existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];		
+	
+	if(inScan)
+	{
+		// If we're scanning then we need to test for duplicates
+		// NB This does not need to be efficient since it should only ever 
+		// happen when the power fails during a write, then only one
+		// chunk should ever be affected.
+	
+		
+		if(existingChunk != 0)
+		{
+			// NB Right now existing chunk will not be real chunkId if the device >= 32MB
+			//    thus we have to do a FindChunkInFile to get the real chunk id.
+			//
+			// We have a duplicate now we need to decide which one to use
+			// To do this we get both sets of tags and compare serial numbers.
+			yaffs_ReadChunkTagsFromNAND(dev,chunkInNAND, &newTags,&newChunkDeleted);
+			
+			
+			// Do a proper find
+			existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);
+
+			if(existingChunk <=0)
+			{
+				//Hoosterman - how did this happen?
+				
+				T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: existing chunk < 0 in scan" TENDSTR)));
+
+			}
+
+			
+			// NB The deleted flags should be false, otherwise the chunks will 
+			// not be loaded during a scan
+			
+			newSerial = newTags.serialNumber;
+			existingSerial = existingTags.serialNumber;
+			
+			if( existingChunk <= 0 ||
+			    ((existingSerial+1) & 3) == newSerial)
+			{
+				// Use new
+				// Delete the old one and drop through to update the tnode
+				yaffs_DeleteChunk(dev,existingChunk,1);
+			}
+			else
+			{
+				// Use existing.
+				// Delete the new one and return early so that the tnode isn't changed
+				yaffs_DeleteChunk(dev,chunkInNAND,1);
+				return YAFFS_OK;
+			}
+		}
+
+	}
+		
+	if(existingChunk == 0)
+	{
+		in->nDataChunks++;
+	}
+	
+	tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits);
+	
+	return YAFFS_OK;
+}
+
+
+
+int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
+{
+    int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);
+    
+    if(chunkInNAND >= 0)
+    {
+		return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL,1);
+	}
+	else
+	{
+		memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
+		return 0;
+	}
+
+}
+
+
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND)
+{
+	int block;
+	int page;
+	yaffs_Spare spare;
+	yaffs_BlockInfo *bi;
+	
+	if(chunkId <= 0) return;	
+	
+	dev->nDeletions++;
+	block = chunkId / dev->nChunksPerBlock;
+	page = chunkId % dev->nChunksPerBlock;
+	
+	if(markNAND)
+	{
+		yaffs_SpareInitialise(&spare);
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+
+                //read data before write, to ensure correct ecc 
+                //if we're using MTD verification under Linux
+                yaffs_ReadChunkFromNAND(dev,chunkId,NULL,&spare,0);
+#endif
+
+		spare.pageStatus = 0; // To mark it as deleted.
+
+	
+		yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
+		yaffs_HandleUpdateChunk(dev,chunkId,&spare);
+	}
+	else
+	{
+			dev->nUnmarkedDeletions++;
+	}	
+	
+	bi = yaffs_GetBlockInfo(dev,block);
+			
+	
+	// Pull out of the management area.
+	// If the whole block became dirty, this will kick off an erasure.
+	if(	bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
+	    bi->blockState == YAFFS_BLOCK_STATE_FULL)
+	{
+		dev->nFreeChunks++;
+
+		yaffs_ClearChunkBit(dev,block,page);
+		bi->pagesInUse--;
+		
+		if(bi->pagesInUse == 0 &&
+	       bi->blockState == YAFFS_BLOCK_STATE_FULL)
+	    {
+	    	yaffs_BlockBecameDirty(dev,block);
+	    }
+
+	}
+	else
+	{
+		// T(("Bad news deleting chunk %d\n",chunkId));
+	}
+	
+}
+
+
+
+
+int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)
+{
+	// Find old chunk Need to do this to get serial number
+	// Write new one and patch into tree.
+	// Invalidate old tags.
+
+    int prevChunkId;
+    yaffs_Tags prevTags;
+    
+    int newChunkId;
+    yaffs_Tags newTags;
+
+    yaffs_Device *dev = in->myDev;    
+
+	yaffs_CheckGarbageCollection(dev);
+
+	// Get the previous chunk at this location in the file if it exists
+    prevChunkId  = yaffs_FindChunkInFile(in,chunkInInode,&prevTags);
+    
+    // Set up new tags
+   	newTags.chunkId = chunkInInode;
+	newTags.objectId = in->objectId;
+	newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
+   	newTags.byteCount = nBytes;
+	newTags.unusedStuff = 0xFFFFFFFF;
+		
+	yaffs_CalcTagsECC(&newTags);
+
+	newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
+	if(newChunkId >= 0)
+	{
+		yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
+		
+		
+		if(prevChunkId >= 0)
+		{
+			yaffs_DeleteChunk(dev,prevChunkId,1);
+	
+		}
+		
+		yaffs_CheckFileSanity(in);
+	}
+	return newChunkId;
+
+
+
+
+
+}
+
+
+// UpdateObjectHeader updates the header on NAND for an object.
+// If name is not NULL, then that new name is used.
+//
+int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force)
+{
+
+	yaffs_Device *dev = in->myDev;
+	
+    int prevChunkId;
+    
+    int newChunkId;
+    yaffs_Tags newTags;
+    __u8 bufferNew[YAFFS_BYTES_PER_CHUNK];
+    __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];
+    
+    yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;
+    yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;
+
+    
+    if(!in->fake || force)
+    {
+  
+		yaffs_CheckGarbageCollection(dev);		
+    
+		memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK);
+    
+		prevChunkId = in->chunkId;
+    
+		if(prevChunkId >= 0)
+		{
+			yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL,1);    	
+		}
+
+		// Header data
+		oh->type = in->variantType;
+		
+		oh->yst_mode = in->yst_mode;
+
+#ifdef CONFIG_YAFFS_WINCE
+		oh->win_atime[0] = in->win_atime[0];
+		oh->win_ctime[0] = in->win_ctime[0];
+		oh->win_mtime[0] = in->win_mtime[0];
+		oh->win_atime[1] = in->win_atime[1];
+		oh->win_ctime[1] = in->win_ctime[1];
+		oh->win_mtime[1] = in->win_mtime[1];
+#else
+		oh->yst_uid = in->yst_uid;
+		oh->yst_gid = in->yst_gid;
+		oh->yst_atime = in->yst_atime;
+		oh->yst_mtime = in->yst_mtime;
+		oh->yst_ctime = in->yst_ctime;
+		oh->yst_rdev = in->yst_rdev;
+#endif	
+		if(in->parent)
+		{
+			oh->parentObjectId = in->parent->objectId;
+		}
+		else
+		{
+			oh->parentObjectId = 0;
+		}
+		
+		//oh->sum = in->sum;
+		if(name && *name)
+		{
+			memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);
+			strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
+		}
+		else if(prevChunkId)
+		{	
+			memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);
+		}
+		else
+		{
+			memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);	
+		}
+	
+		switch(in->variantType)
+		{
+			case YAFFS_OBJECT_TYPE_UNKNOWN: 	
+				// Should not happen
+				break;
+			case YAFFS_OBJECT_TYPE_FILE:
+				oh->fileSize = in->variant.fileVariant.fileSize;
+				break;
+			case YAFFS_OBJECT_TYPE_HARDLINK:
+				oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;
+				break;
+			case YAFFS_OBJECT_TYPE_SPECIAL:	
+				// Do nothing
+				break;
+			case YAFFS_OBJECT_TYPE_DIRECTORY:	
+				// Do nothing
+				break;
+			case YAFFS_OBJECT_TYPE_SYMLINK:
+				strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);
+				oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
+				break;
+		}
+
+		// Tags
+		in->serial++;
+		newTags.chunkId = 0;
+		newTags.objectId = in->objectId;
+		newTags.serialNumber = in->serial;
+		newTags.byteCount =   0xFFFFFFFF;
+		newTags.unusedStuff = 0xFFFFFFFF;
+	
+		yaffs_CalcTagsECC(&newTags);
+	
+
+		// Create new chunk in NAND
+		newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags, (prevChunkId >= 0) ? 1 : 0 );
+    
+		if(newChunkId >= 0)
+		{
+		
+			in->chunkId = newChunkId;		
+		
+			if(prevChunkId >= 0)
+			{
+				yaffs_DeleteChunk(dev,prevChunkId,1);
+			}
+		
+			in->dirty = 0;
+		}
+		
+		return newChunkId;
+
+    }
+    return 0;
+}
+
+
+/////////////////////// Short Operations Cache ////////////////////////////////
+//	In many siturations where there is no high level buffering (eg WinCE) a lot of
+//	reads might be short sequential reads, and a lot of writes may be short 
+//  sequential writes. eg. scanning/writing a jpeg file.
+//	In these cases, a short read/write cache can provide a huge perfomance benefit 
+//  with dumb-as-a-rock code.
+//  There are a limited number (~10) of cache chunks per device so that we don't
+//  need a very intelligent search.
+
+
+
+
+
+static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
+{
+	yaffs_Device *dev = obj->myDev;
+	int lowest;
+	int i;
+	yaffs_ChunkCache *cache;
+	int chunkWritten = 0;
+	int nBytes;
+	int nCaches = obj->myDev->nShortOpCaches;
+	
+	if  (nCaches > 0)
+	{
+		do{
+			cache = NULL;
+		
+			// Find the dirty cache for this object with the lowest chunk id.
+			for(i = 0; i < nCaches; i++)
+			{
+				if(dev->srCache[i].object == obj &&
+				dev->srCache[i].dirty)
+				{
+					if(!cache ||  dev->srCache[i].chunkId < lowest)
+					{
+						cache = &dev->srCache[i];
+						lowest = cache->chunkId;
+					}
+				}
+			}
+		
+			if(cache)
+			{
+				//Write it out
+
+#if 0
+				nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
+			
+				if(nBytes > YAFFS_BYTES_PER_CHUNK)
+				{
+					nBytes= YAFFS_BYTES_PER_CHUNK;
+				}
+#endif			
+				chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
+															cache->chunkId,
+															cache->data,
+															cache->nBytes,1);
+
+				cache->dirty = 0;
+				cache->object = NULL;
+			}
+		
+		} while(cache && chunkWritten > 0);
+	
+		if(cache)
+		{
+			//Hoosterman, disk full while writing cache out.
+			T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
+
+		}
+	}	
+		
+}
+
+
+// Grab us a chunk for use.
+// First look for an empty one. 
+// Then look for the least recently used non-dirty one.
+// Then look for the least recently used dirty one...., flush and look again.
+static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev)
+{
+	int i;
+	int usage;
+	int theOne;
+	
+	if(dev->nShortOpCaches > 0)
+	{
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(!dev->srCache[i].object)
+			{
+				//T(("Grabbing empty %d\n",i));
+				
+				//printf("Grabbing empty %d\n",i);
+			
+				return &dev->srCache[i];
+			}
+		}
+		
+		return NULL;
+	
+		theOne = -1; 
+		usage = 0; // just to stop the compiler grizzling
+	
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(!dev->srCache[i].dirty &&
+			((dev->srCache[i].lastUse < usage  && theOne >= 0)|| 
+				theOne < 0))
+			{
+				usage = dev->srCache[i].lastUse;
+				theOne = i;
+			}
+		}
+	
+		//T(("Grabbing non-empty %d\n",theOne));
+		
+		//if(theOne >= 0) printf("Grabbed non-empty cache %d\n",theOne);
+		
+		return  theOne >= 0 ?  &dev->srCache[theOne] : NULL;
+	}
+	else
+	{
+		return NULL;
+	}
+	
+}
+
+
+static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)
+{
+	yaffs_ChunkCache *cache;
+	yaffs_Object *theObj;
+	int usage;
+	int i;
+	int pushout;
+	
+	if(dev->nShortOpCaches > 0)
+	{
+		// Try find a non-dirty one...
+	
+		cache = yaffs_GrabChunkCacheWorker(dev);
+	
+		if(!cache)
+		{
+			// They were all dirty, find the last recently used object and flush
+			// its cache, then  find again.
+			// NB what's here is not very accurate, we actually flush the object
+			// the last recently used page.
+		
+			theObj = dev->srCache[0].object;
+			usage = dev->srCache[0].lastUse;
+			cache = &dev->srCache[0];
+			pushout = 0;
+	
+			for(i = 1; i < dev->nShortOpCaches; i++)
+			{
+				if( dev->srCache[i].object && 
+					dev->srCache[i].lastUse < usage)
+				{
+					usage  = dev->srCache[i].lastUse;
+					theObj = dev->srCache[i].object;
+					cache = &dev->srCache[i];
+					pushout = i;
+				}
+			}
+		
+			if(!cache || cache->dirty)
+			{
+			
+				//printf("Dirty ");
+				yaffs_FlushFilesChunkCache(theObj);
+		
+				// Try again
+				cache = yaffs_GrabChunkCacheWorker(dev);
+			}
+			else
+			{
+				//printf(" pushout %d\n",pushout);
+			}
+			
+		}
+
+		return cache;
+	}
+	else
+		return NULL;
+
+}
+
+
+// Find a cached chunk
+static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunkId)
+{
+	yaffs_Device *dev = obj->myDev;
+	int i;
+	if(dev->nShortOpCaches > 0)
+	{
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(dev->srCache[i].object == obj && 
+			dev->srCache[i].chunkId == chunkId)
+			{
+				dev->cacheHits++;
+			
+				return &dev->srCache[i];
+			}
+		}
+	}
+	return NULL;
+}
+
+// Mark the chunk for the least recently used algorithym
+static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite)
+{
+
+	if(dev->nShortOpCaches > 0)
+	{
+		if( dev->srLastUse < 0 || 
+			dev->srLastUse > 100000000)
+		{
+			// Reset the cache usages
+			int i;
+			for(i = 1; i < dev->nShortOpCaches; i++)
+			{
+				dev->srCache[i].lastUse = 0;
+			}
+			dev->srLastUse = 0;
+		}
+
+		dev->srLastUse++;
+	
+		cache->lastUse = dev->srLastUse;
+
+		if(isAWrite)
+		{
+			cache->dirty = 1;
+		}
+	}
+}
+
+// Invalidate a single cache page.
+// Do this when a whole page gets written,
+// ie the short cache for this page is no longer valid.
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId)
+{
+	if(object->myDev->nShortOpCaches > 0)
+	{
+		yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);
+
+		if(cache)
+		{
+			cache->object = NULL;
+		}
+	}
+}
+
+
+// Invalidate all the cache pages associated with this object
+// Do this whenever ther file is deleted or resized.
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)
+{
+	int i;
+	yaffs_Device *dev = in->myDev;
+	
+	if(dev->nShortOpCaches > 0)
+	{ 
+		// Now invalidate it.
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(dev->srCache[i].object == in)
+			{
+				dev->srCache[i].object = NULL;
+			}
+		}
+	}
+}
+
+
+
+
+
+///////////////////////// File read/write ///////////////////////////////
+// Read and write have very similar structures.
+// In general the read/write has three parts to it
+// * An incomplete chunk to start with (if the read/write is not chunk-aligned)
+// * Some complete chunks
+// * An incomplete chunk to end off with
+//
+// Curve-balls: the first chunk might also be the last chunk.
+
+int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)
+{
+	
+	
+	int chunk;
+	int start;
+	int nToCopy;
+	int n = nBytes;
+	int nDone = 0;
+	yaffs_ChunkCache *cache;
+	
+	yaffs_Device *dev;
+	
+	dev = in->myDev;
+	
+	while(n > 0)
+	{
+		chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1
+		start = offset % YAFFS_BYTES_PER_CHUNK;
+
+		// OK now check for the curveball where the start and end are in
+		// the same chunk.	
+		if(	(start + n) < YAFFS_BYTES_PER_CHUNK)
+		{
+			nToCopy = n;
+		}
+		else
+		{
+			nToCopy = YAFFS_BYTES_PER_CHUNK - start;
+		}
+	
+		cache = yaffs_FindChunkCache(in,chunk);
+		
+		// If the chunk is already in the cache or it is less than a whole chunk
+		// then use the cache (if there is caching)
+		// else bypass the cache.
+		if( cache || nToCopy != YAFFS_BYTES_PER_CHUNK)
+		{
+			if(dev->nShortOpCaches > 0)
+			{
+				
+				// If we can't find the data in the cache, then load it up.
+				
+				if(!cache)
+				{
+					cache = yaffs_GrabChunkCache(in->myDev);
+					cache->object = in;
+					cache->chunkId = chunk;
+					cache->dirty = 0;
+					yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
+					cache->nBytes = 0;	
+				}
+			
+				yaffs_UseChunkCache(dev,cache,0);
+
+				memcpy(buffer,&cache->data[start],nToCopy);
+			}
+			else
+			{
+				// Read into the local buffer then copy...
+				yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);		
+				memcpy(buffer,&dev->localBuffer[start],nToCopy);
+			}
+
+		}
+		else
+		{
+#ifdef CONFIG_YAFFS_WINCE
+			
+			// Under WinCE can't do direct transfer. Need to use a local buffer.
+			// This is because we otherwise screw up WinCE's memory mapper
+			yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
+			memcpy(buffer,dev->localBuffer,YAFFS_BYTES_PER_CHUNK);
+#else
+			// A full chunk. Read directly into the supplied buffer.
+			yaffs_ReadChunkDataFromObject(in,chunk,buffer);
+#endif
+		}
+		
+		n -= nToCopy;
+		offset += nToCopy;
+		buffer += nToCopy;
+		nDone += nToCopy;
+		
+	}
+	
+	return nDone;
+}
+
+
+
+int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)
+{	
+	
+	int chunk;
+	int start;
+	int nToCopy;
+	int n = nBytes;
+	int nDone = 0;
+	int nToWriteBack;
+	int startOfWrite = offset;
+	int chunkWritten = 0;
+	int nBytesRead;
+	
+	yaffs_Device *dev;
+	
+	dev = in->myDev;
+	
+	
+	while(n > 0 && chunkWritten >= 0)
+	{
+		chunk = offset / YAFFS_BYTES_PER_CHUNK + 1;
+		start = offset % YAFFS_BYTES_PER_CHUNK;
+		
+
+		// OK now check for the curveball where the start and end are in
+		// the same chunk.
+		
+		if(	(start + n) < YAFFS_BYTES_PER_CHUNK)
+		{
+			nToCopy = n;
+			
+			// Now folks, to calculate how many bytes to write back....
+			// If we're overwriting and not writing to then end of file then
+			// we need to write back as much as was there before.
+			
+			nBytesRead = in->variant.fileVariant.fileSize - ((chunk -1) * YAFFS_BYTES_PER_CHUNK);
+			
+			if(nBytesRead > YAFFS_BYTES_PER_CHUNK)
+			{
+				nBytesRead = YAFFS_BYTES_PER_CHUNK;
+			}
+			
+			nToWriteBack = (nBytesRead > (start + n)) ? nBytesRead : (start +n);
+			
+		}
+		else
+		{
+			nToCopy = YAFFS_BYTES_PER_CHUNK - start;
+			nToWriteBack = YAFFS_BYTES_PER_CHUNK;
+		}
+	
+		if(nToCopy != YAFFS_BYTES_PER_CHUNK)
+		{
+			// An incomplete start or end chunk (or maybe both start and end chunk)
+			if(dev->nShortOpCaches > 0)
+			{
+				yaffs_ChunkCache *cache;
+				// If we can't find the data in the cache, then load it up.
+				cache = yaffs_FindChunkCache(in,chunk);
+				if(!cache && yaffs_CheckSpaceForChunkCache(in->myDev))
+				{
+					cache = yaffs_GrabChunkCache(in->myDev);
+					cache->object = in;
+					cache->chunkId = chunk;
+					cache->dirty = 0;
+					yaffs_ReadChunkDataFromObject(in,chunk,cache->data);		
+				}
+			
+				if(cache)
+				{	
+					yaffs_UseChunkCache(dev,cache,1);
+					memcpy(&cache->data[start],buffer,nToCopy);
+					cache->nBytes = nToWriteBack;
+				}
+				else
+				{
+					chunkWritten = -1; // fail the write
+				}
+			}
+			else
+			{
+				// An incomplete start or end chunk (or maybe both start and end chunk)
+				// Read into the local buffer then copy, then copy over and write back.
+		
+				yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
+				
+				memcpy(&dev->localBuffer[start],buffer,nToCopy);
+			
+				chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,nToWriteBack,0);
+			
+				//T(("Write with readback to chunk %d %d  start %d  copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
+			}
+			
+		}
+		else
+		{
+			
+#ifdef CONFIG_YAFFS_WINCE
+			// Under WinCE can't do direct transfer. Need to use a local buffer.
+			// This is because we otherwise screw up WinCE's memory mapper
+			memcpy(dev->localBuffer,buffer,YAFFS_BYTES_PER_CHUNK);
+			chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,YAFFS_BYTES_PER_CHUNK,0);
+#else
+			// A full chunk. Write directly from the supplied buffer.
+			chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);
+#endif
+			// Since we've overwritten the cached data, we better invalidate it.
+			yaffs_InvalidateChunkCache(in,chunk);
+			//T(("Write to chunk %d %d\n",chunk,chunkWritten));
+		}
+		
+		if(chunkWritten >= 0)
+		{
+			n -= nToCopy;
+			offset += nToCopy;
+			buffer += nToCopy;
+			nDone += nToCopy;
+		}
+		
+	}
+	
+	// Update file object
+	
+	if((startOfWrite + nDone) > in->variant.fileVariant.fileSize)
+	{
+		in->variant.fileVariant.fileSize = (startOfWrite + nDone);
+	}
+	
+	in->dirty = 1;
+	
+	return nDone;
+}
+
+
+int yaffs_ResizeFile(yaffs_Object *in, int newSize)
+{
+	int i;
+	int chunkId;
+	int oldFileSize = in->variant.fileVariant.fileSize;
+	int sizeOfPartialChunk = newSize % YAFFS_BYTES_PER_CHUNK;
+	
+	yaffs_Device *dev = in->myDev;
+	
+
+	yaffs_FlushFilesChunkCache(in);	
+	yaffs_InvalidateWholeChunkCache(in);
+	
+	if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
+	{
+		return yaffs_GetFileSize(in);
+	}
+	
+	if(newSize < oldFileSize)
+	{
+		
+		int lastDel = 1 + (oldFileSize-1)/YAFFS_BYTES_PER_CHUNK;
+		
+		int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/
+							YAFFS_BYTES_PER_CHUNK;
+
+		// Delete backwards so that we don't end up with holes if
+		// power is lost part-way through the operation.
+		for(i = lastDel; i >= startDel; i--)
+		{
+			// NB this could be optimised somewhat,
+			// eg. could retrieve the tags and write them without
+			// using yaffs_DeleteChunk
+
+			chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
+			if(chunkId < (dev->internalStartBlock * 32) || chunkId >= ((dev->internalEndBlock+1) * 32))
+			{
+				//T(("Found daft chunkId %d for %d\n",chunkId,i));
+			}
+			else
+			{
+				in->nDataChunks--;
+				yaffs_DeleteChunk(dev,chunkId,1);
+			}
+		}
+		
+		
+		if(sizeOfPartialChunk != 0)
+		{
+			int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;
+			
+			// Got to read and rewrite the last chunk with its new size.
+			// NB Got to zero pad to nuke old data
+			yaffs_ReadChunkDataFromObject(in,lastChunk,dev->localBuffer);
+			memset(dev->localBuffer + sizeOfPartialChunk,0, YAFFS_BYTES_PER_CHUNK - sizeOfPartialChunk);
+
+			yaffs_WriteChunkDataToObject(in,lastChunk,dev->localBuffer,sizeOfPartialChunk,1);
+				
+		}
+		
+		in->variant.fileVariant.fileSize = newSize;
+		
+		yaffs_PruneFileStructure(dev,&in->variant.fileVariant);
+		
+		return newSize;
+		
+	}
+	else
+	{
+		return oldFileSize;
+	}
+}
+
+
+loff_t yaffs_GetFileSize(yaffs_Object *obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	
+	switch(obj->variantType)
+	{
+		case YAFFS_OBJECT_TYPE_FILE: 
+			return obj->variant.fileVariant.fileSize;
+		case YAFFS_OBJECT_TYPE_SYMLINK:
+			return strlen(obj->variant.symLinkVariant.alias);
+		default:
+			return 0;
+	}
+}
+
+
+
+// yaffs_FlushFile() updates the file's
+// objectId in NAND
+
+int yaffs_FlushFile(yaffs_Object *in, int updateTime)
+{
+	int retVal;
+	if(in->dirty)
+	{
+		//T(("flushing object header\n"));
+		
+		yaffs_FlushFilesChunkCache(in);
+		if(updateTime)
+		{
+#ifdef CONFIG_YAFFS_WINCE
+			yfsd_WinFileTimeNow(in->win_mtime);
+#else
+			in->yst_mtime = Y_CURRENT_TIME;
+#endif
+		}
+
+		retVal = (yaffs_UpdateObjectHeader(in,NULL,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
+	}
+	else
+	{
+		retVal = YAFFS_OK;
+	}
+	
+	return retVal;
+	
+}
+
+
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
+{
+
+	// First off, invalidate the file's data in the cache, without flushing.
+	yaffs_InvalidateWholeChunkCache(in);
+	
+	yaffs_RemoveObjectFromDirectory(in);
+	yaffs_DeleteChunk(in->myDev,in->chunkId,1);
+	in->chunkId = -1;
+#if 0
+#ifdef __KERNEL__
+	if(in->myInode)
+	{
+		in->myInode->u.generic_ip = NULL;
+		in->myInode = NULL;
+	}
+#endif
+#endif
+	yaffs_FreeObject(in);
+	return YAFFS_OK;
+
+}
+
+// yaffs_DeleteFile deletes the whole file data
+// and the inode associated with the file.
+// It does not delete the links associated with the file.
+static int yaffs_UnlinkFile(yaffs_Object *in)
+{
+
+#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION
+
+	// Delete the file data & tnodes
+
+	 yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);
+	 
+
+	yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
+	
+	return  yaffs_DoGenericObjectDeletion(in);
+#else
+	int retVal;
+	int immediateDeletion=0;
+	retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0);
+	if(1 || // Ignore the result of the change name. This will only fail
+			// if the disk was completely full (an error condition)
+			// We ignore the error so we can delete files to recover the disk
+	   retVal == YAFFS_OK)
+	{
+		//in->unlinked = 1;
+		//in->myDev->nUnlinkedFiles++;
+		//in->renameAllowed = 0;
+#ifdef __KERNEL__
+		if(!in->myInode)
+		{
+			// Might be open at present,
+			// Caught by delete_inode in yaffs_fs.c
+			immediateDeletion = 1;
+
+		}
+#else
+		if(in->inUse <= 0)
+		{
+			immediateDeletion = 1;
+
+		}
+#endif
+		
+		if(immediateDeletion)
+		{
+			T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));
+			in->deleted=1;
+			in->myDev->nDeletedFiles++;
+			yaffs_SoftDeleteFile(in);
+		}
+	
+	}
+	return retVal;
+
+	
+#endif
+}
+
+int yaffs_DeleteFile(yaffs_Object *in)
+{
+	int retVal = YAFFS_OK;
+	
+	if(in->nDataChunks > 0)
+	{
+		// Use soft deletion
+		if(!in->unlinked)
+		{
+			retVal = yaffs_UnlinkFile(in);
+		}
+		if(retVal == YAFFS_OK && 
+		in->unlinked &&
+		!in->deleted)
+		{
+			in->deleted = 1;
+			in->myDev->nDeletedFiles++;
+			yaffs_SoftDeleteFile(in);
+		}
+		return in->deleted ? YAFFS_OK : YAFFS_FAIL;	
+	}
+	else
+	{
+		// The file has no data chunks so we toss it immediately
+		yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
+		in->variant.fileVariant.top = NULL;
+		yaffs_DoGenericObjectDeletion(in);	
+		
+		return YAFFS_OK;	
+	}
+}
+
+static int yaffs_DeleteDirectory(yaffs_Object *in)
+{
+	//First check that the directory is empty.
+	if(list_empty(&in->variant.directoryVariant.children))
+	{
+		return  yaffs_DoGenericObjectDeletion(in);
+	}
+	
+	return YAFFS_FAIL;
+	
+}
+
+static int yaffs_DeleteSymLink(yaffs_Object *in)
+{
+	YFREE(in->variant.symLinkVariant.alias);
+
+	return  yaffs_DoGenericObjectDeletion(in);
+}
+
+static int yaffs_DeleteHardLink(yaffs_Object *in)
+{
+	// remove this hardlink from the list assocaited with the equivalent
+	// object
+	list_del(&in->hardLinks);
+	return  yaffs_DoGenericObjectDeletion(in);	
+}
+
+
+static void yaffs_AbortHalfCreatedObject(yaffs_Object *obj)
+{
+	switch(obj->variantType)
+	{
+		case YAFFS_OBJECT_TYPE_FILE: yaffs_DeleteFile(obj); break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY: yaffs_DeleteDirectory(obj); break;
+		case YAFFS_OBJECT_TYPE_SYMLINK: yaffs_DeleteSymLink(obj); break;
+		case YAFFS_OBJECT_TYPE_HARDLINK: yaffs_DeleteHardLink(obj); break;
+		case YAFFS_OBJECT_TYPE_SPECIAL: yaffs_DoGenericObjectDeletion(obj); break;
+		case YAFFS_OBJECT_TYPE_UNKNOWN: break; // should not happen.
+	}
+}
+
+
+static int yaffs_UnlinkWorker(yaffs_Object *obj)
+{
+
+	
+	if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+	{
+		return  yaffs_DeleteHardLink(obj);
+	}
+	else if(!list_empty(&obj->hardLinks))
+	{	
+		// Curve ball: We're unlinking an object that has a hardlink.
+		//
+		//	This problem arises because we are not strictly following
+		//  The Linux link/inode model.
+		//
+		// We can't really delete the object.
+		// Instead, we do the following:
+		// - Select a hardlink.
+		// - Unhook it from the hard links
+		// - Unhook it from its parent directory (so that the rename can work)
+		// - Rename the object to the hardlink's name.
+		// - Delete the hardlink
+		
+		
+		yaffs_Object *hl;
+		int retVal;
+		char name[YAFFS_MAX_NAME_LENGTH+1];
+		
+		hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks);
+		
+		list_del_init(&hl->hardLinks);
+		list_del_init(&hl->siblings);
+		
+		yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);
+		
+		retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0);
+		
+		if(retVal == YAFFS_OK)
+		{
+			retVal = yaffs_DoGenericObjectDeletion(hl);
+		}
+		return retVal;
+				
+	}
+	else
+	{
+		switch(obj->variantType)
+		{
+			case YAFFS_OBJECT_TYPE_FILE:
+				return yaffs_UnlinkFile(obj);
+				break;
+			case YAFFS_OBJECT_TYPE_DIRECTORY:
+				return yaffs_DeleteDirectory(obj);
+				break;
+			case YAFFS_OBJECT_TYPE_SYMLINK:
+				return yaffs_DeleteSymLink(obj);
+				break;
+			case YAFFS_OBJECT_TYPE_SPECIAL:
+				return yaffs_DoGenericObjectDeletion(obj);
+				break;
+			case YAFFS_OBJECT_TYPE_HARDLINK:
+			case YAFFS_OBJECT_TYPE_UNKNOWN:
+			default:
+				return YAFFS_FAIL;
+		}
+	}
+}
+
+int yaffs_Unlink(yaffs_Object *dir, const char *name)
+{
+	yaffs_Object *obj;
+	
+	 obj = yaffs_FindObjectByName(dir,name);
+	 
+	 if(obj && obj->unlinkAllowed)
+	 {
+	 	return yaffs_UnlinkWorker(obj);
+	 }
+	 
+	 return YAFFS_FAIL;
+	
+}
+
+//////////////// Initialisation Scanning /////////////////
+
+
+
+// For now we use the SmartMedia check.
+// We look at the blockStatus byte in the first two chunks
+// These must be 0xFF to pass as OK.
+// todo: this function needs to be modifyable foir different NAND types
+// and different chunk sizes.  Suggest make this into a per-device configurable
+// function.
+static int yaffs_IsBlockBad(yaffs_Device *dev, int blk)
+{
+	yaffs_Spare spare;
+	
+	yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock,NULL,&spare,1);
+#if 1
+	if(yaffs_CountBits(spare.blockStatus) < 7)
+	{
+		return 1;
+	}
+#else
+	if(spare.blockStatus != 0xFF)
+	{
+		return 1;
+	}
+#endif
+	yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock + 1,NULL,&spare,1);
+
+#if 1
+	if(yaffs_CountBits(spare.blockStatus) < 7)
+	{
+		return 1;
+	}
+#else
+	if(spare.blockStatus != 0xFF)
+	{
+		return 1;
+	}
+#endif
+	
+	return 0;
+	
+}
+
+static int yaffs_Scan(yaffs_Device *dev)
+{
+	yaffs_Spare spare;
+	yaffs_Tags tags;
+	int blk;
+	int chunk;
+	int c;
+	int deleted;
+	yaffs_BlockState state;
+	yaffs_Object *hardList = NULL;
+	yaffs_Object *hl;
+	yaffs_BlockInfo *bi;
+
+	
+	yaffs_ObjectHeader *oh;
+	yaffs_Object *in;
+	yaffs_Object *parent;
+	
+	__u8 chunkData[YAFFS_BYTES_PER_CHUNK];
+	
+	
+	// Scan all the blocks...
+	
+	for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
+	{
+		deleted = 0;
+		bi = yaffs_GetBlockInfo(dev,blk);
+		yaffs_ClearChunkBits(dev,blk);
+		bi->pagesInUse = 0;
+		bi->softDeletions = 0;
+		state = YAFFS_BLOCK_STATE_SCANNING;
+		
+		
+		if(yaffs_IsBlockBad(dev,blk))
+		{
+			state = YAFFS_BLOCK_STATE_DEAD;
+			T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
+		}
+		
+		// Read each chunk in the block.
+		
+		for(c = 0; c < dev->nChunksPerBlock && 
+				   state == YAFFS_BLOCK_STATE_SCANNING; c++)
+		{
+			// Read the spare area and decide what to do
+			chunk = blk * dev->nChunksPerBlock + c;
+			
+			yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare,1);
+
+			
+			// This block looks ok, now what's in this chunk?
+			yaffs_GetTagsFromSpare(dev,&spare,&tags);
+			
+			if(yaffs_CountBits(spare.pageStatus) < 6)
+			{
+				// A deleted chunk
+				deleted++;
+				dev->nFreeChunks ++;
+				//T((" %d %d deleted\n",blk,c));
+			}
+			else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID)
+			{
+				// An unassigned chunk in the block
+				// This means that either the block is empty or 
+				// this is the one being allocated from
+				
+				if(c == 0)
+				{
+					// the block is unused
+					state = YAFFS_BLOCK_STATE_EMPTY;
+					dev->nErasedBlocks++;
+				}
+				else
+				{
+					// this is the block being allocated from
+					T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
+					state = YAFFS_BLOCK_STATE_ALLOCATING;
+					dev->allocationBlock = blk;
+					dev->allocationPage = c;
+				}
+
+				dev->nFreeChunks += (dev->nChunksPerBlock - c);
+			}
+			else if(tags.chunkId > 0)
+			{
+				int endpos;
+				// A data chunk.
+				yaffs_SetChunkBit(dev,blk,c);
+				bi->pagesInUse++;
+								
+				in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
+				// PutChunkIntoFIle checks for a clash (two data chunks with
+				// the same chunkId).
+				yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
+				endpos = (tags.chunkId - 1)* YAFFS_BYTES_PER_CHUNK + tags.byteCount;
+				if(in->variant.fileVariant.scannedFileSize <endpos)
+				{
+					in->variant.fileVariant.scannedFileSize = endpos;
+#ifndef CONFIG_YAFFS_USE_HEADER_FILE_SIZE
+						in->variant.fileVariant.fileSize = 	
+							in->variant.fileVariant.scannedFileSize;
+#endif
+
+				}
+				//T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));	
+			}
+			else
+			{
+				// chunkId == 0, so it is an ObjectHeader.
+				// Thus, we read in the object header and make the object
+				yaffs_SetChunkBit(dev,blk,c);
+				bi->pagesInUse++;
+							
+				yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1);
+				
+				oh = (yaffs_ObjectHeader *)chunkData;
+				
+				in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
+				
+				if(in->valid)
+				{
+					// We have already filled this one. We have a duplicate and need to resolve it.
+					
+					unsigned existingSerial = in->serial;
+					unsigned newSerial = tags.serialNumber;
+					
+					if(((existingSerial+1) & 3) == newSerial)
+					{
+						// Use new one - destroy the exisiting one
+						yaffs_DeleteChunk(dev,in->chunkId,1);
+						in->valid = 0;
+					}
+					else
+					{
+						// Use existing - destroy this one.
+						yaffs_DeleteChunk(dev,chunk,1);
+					}
+				}
+				
+				if(!in->valid &&
+				   (tags.objectId == YAFFS_OBJECTID_ROOT ||
+				    tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))
+				{
+					// We only load some info, don't fiddle with directory structure
+					in->valid = 1;
+					in->variantType = oh->type;
+	
+					in->yst_mode  = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+					in->win_atime[0] = oh->win_atime[0];
+					in->win_ctime[0] = oh->win_ctime[0];
+					in->win_mtime[0] = oh->win_mtime[0];
+					in->win_atime[1] = oh->win_atime[1];
+					in->win_ctime[1] = oh->win_ctime[1];
+					in->win_mtime[1] = oh->win_mtime[1];
+#else
+					in->yst_uid   = oh->yst_uid;
+					in->yst_gid   = oh->yst_gid;
+					in->yst_atime = oh->yst_atime;
+					in->yst_mtime = oh->yst_mtime;
+					in->yst_ctime = oh->yst_ctime;
+					in->yst_rdev = oh->yst_rdev;
+#endif
+					in->chunkId  = chunk;
+
+				}
+				else if(!in->valid)
+				{
+					// we need to load this info
+				
+					in->valid = 1;
+					in->variantType = oh->type;
+	
+					in->yst_mode  = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+					in->win_atime[0] = oh->win_atime[0];
+					in->win_ctime[0] = oh->win_ctime[0];
+					in->win_mtime[0] = oh->win_mtime[0];
+					in->win_atime[1] = oh->win_atime[1];
+					in->win_ctime[1] = oh->win_ctime[1];
+					in->win_mtime[1] = oh->win_mtime[1];
+#else
+					in->yst_uid   = oh->yst_uid;
+					in->yst_gid   = oh->yst_gid;
+					in->yst_atime = oh->yst_atime;
+					in->yst_mtime = oh->yst_mtime;
+					in->yst_ctime = oh->yst_ctime;
+					in->yst_rdev = oh->yst_rdev;
+#endif
+					in->chunkId  = chunk;
+
+					yaffs_SetObjectName(in,oh->name);
+					in->dirty = 0;
+							
+					// directory stuff...
+					// hook up to parent
+	
+					parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);
+					if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)
+					{
+						// Set up as a directory
+						parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;
+						INIT_LIST_HEAD(&parent->variant.directoryVariant.children);
+					}
+					else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+					{
+						// Hoosterman, another problem....
+						// We're trying to use a non-directory as a directory
+						
+						T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." TENDSTR)));
+						parent = dev->lostNFoundDir;
+
+					}
+				
+					yaffs_AddObjectToDirectory(parent,in);
+					if(parent == dev->unlinkedDir)
+					{
+						in->deleted = 1; // If it is unlinked at start up then it wants deleting
+						dev->nDeletedFiles++;
+					}
+				
+					// Note re hardlinks.
+					// Since we might scan a hardlink before its equivalent object is scanned
+					// we put them all in a list.
+					// After scanning is complete, we should have all the objects, so we run through this
+					// list and fix up all the chains.		
+	
+					switch(in->variantType)
+					{
+						case YAFFS_OBJECT_TYPE_UNKNOWN: 	// Todo got a problem
+							break;
+						case YAFFS_OBJECT_TYPE_FILE:
+#ifdef CONFIG_YAFFS_USE_HEADER_FILE_SIZE
+							in->variant.fileVariant.fileSize = oh->fileSize;
+#endif
+							break;
+						case YAFFS_OBJECT_TYPE_HARDLINK:
+							in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
+							in->hardLinks.next = (struct list_head *)hardList;
+							hardList = in;
+							break;
+						case YAFFS_OBJECT_TYPE_DIRECTORY:	// Do nothing
+							break;
+						case YAFFS_OBJECT_TYPE_SPECIAL:	// Do nothing
+							break;
+						case YAFFS_OBJECT_TYPE_SYMLINK: 	// Do nothing
+							in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
+							break;
+					}
+					//T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));	
+				}
+			}
+		}
+		
+		if(state == YAFFS_BLOCK_STATE_SCANNING)
+		{
+			// If we got this far while scanning, then the block is fully allocated.
+			state = YAFFS_BLOCK_STATE_FULL;	
+		}
+		
+		bi->blockState = state;
+		
+		// Now let's see if it was dirty
+		if(	bi->pagesInUse == 0 &&
+	        bi->blockState == YAFFS_BLOCK_STATE_FULL)
+	    {
+	    	yaffs_BlockBecameDirty(dev,blk);
+	    }
+
+	}
+	
+	// Fix up the hard link chains.
+	// We should now have scanned all the objects, now it's time to add these 
+	// hardlinks.
+	while(hardList)
+	{
+		hl = hardList;
+		hardList = (yaffs_Object *)(hardList->hardLinks.next);
+		
+		in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId);
+		
+		if(in)
+		{
+			// Add the hardlink pointers
+			hl->variant.hardLinkVariant.equivalentObject=in;
+			list_add(&hl->hardLinks,&in->hardLinks);
+		}
+		else
+		{
+			//Todo Need to report/handle this better.
+			// Got a problem... hardlink to a non-existant object
+			hl->variant.hardLinkVariant.equivalentObject=NULL;
+			INIT_LIST_HEAD(&hl->hardLinks);
+			
+		}
+		
+	}
+	
+	{
+		struct list_head *i;	
+		struct list_head *n;
+			
+		yaffs_Object *l;
+		// Soft delete all the unlinked files
+		list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
+		{
+			if(i)
+			{
+				l = list_entry(i, yaffs_Object,siblings);
+				if(l->deleted)
+				{
+					yaffs_SoftDeleteFile(l);		
+				}
+			}
+		}	
+	}
+	
+	
+	return YAFFS_OK;
+}
+
+
+////////////////////////// Directory Functions /////////////////////////
+
+
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)
+{
+
+	if(obj->siblings.prev == NULL)
+	{
+		// Not initialised
+		INIT_LIST_HEAD(&obj->siblings);
+		
+	}
+	else if(!list_empty(&obj->siblings))
+	{
+		// If it is holed up somewhere else, un hook it
+		list_del_init(&obj->siblings);
+	}
+	// Now add it
+	list_add(&obj->siblings,&directory->variant.directoryVariant.children);
+	obj->parent = directory;
+	
+	if(directory == obj->myDev->unlinkedDir)
+	{
+		obj->unlinked = 1;
+		obj->myDev->nUnlinkedFiles++;
+		obj->renameAllowed = 0;
+	}
+}
+
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
+{
+	list_del_init(&obj->siblings);
+	obj->parent = NULL;
+}
+
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name)
+{
+	int sum;
+	
+	struct list_head *i;
+	char buffer[YAFFS_MAX_NAME_LENGTH+1];
+	
+	yaffs_Object *l;
+	
+	sum = yaffs_CalcNameSum(name);
+	
+	list_for_each(i,&directory->variant.directoryVariant.children)
+	{
+		if(i)
+		{
+			l = list_entry(i, yaffs_Object,siblings);
+		
+			// Special case for lost-n-found
+			if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
+			{
+				if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
+				{
+					return l;
+				}
+			}
+			else if(yaffs_SumCompare(l->sum, sum))
+			{
+				// Do a real check
+				yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);
+				if(yaffs_strcmp(name,buffer) == 0)
+				{
+					return l;
+				}
+			
+			}			
+		}
+	}
+	
+	return NULL;
+}
+
+
+int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *))
+{
+	struct list_head *i;	
+	yaffs_Object *l;
+	
+	
+	list_for_each(i,&theDir->variant.directoryVariant.children)
+	{
+		if(i)
+		{
+			l = list_entry(i, yaffs_Object,siblings);
+			if(l && !fn(l))
+			{
+				return YAFFS_FAIL;
+			}
+		}
+	}
+	
+	return YAFFS_OK;
+
+}
+
+
+// GetEquivalentObject dereferences any hard links to get to the
+// actual object.
+
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)
+{
+	if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+	{
+		// We want the object id of the equivalent object, not this one
+		obj = obj->variant.hardLinkVariant.equivalentObject;
+	}
+	return obj;
+
+}
+
+int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize)
+{
+	memset(name,0,buffSize);
+	
+	if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND)
+	{
+		strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1);
+	}
+	else if(obj->chunkId <= 0)
+	{
+		char locName[20];
+		// make up a name
+		sprintf(locName,"%s%d",YAFFS_LOSTNFOUND_PREFIX,obj->objectId);
+		strncpy(name,locName,buffSize - 1);
+
+	}
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+	else if(obj->shortName[0])
+	{
+		strcpy(name,obj->shortName);
+	}
+#endif
+	else
+	{
+		__u8 buffer[YAFFS_BYTES_PER_CHUNK];
+		yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
+
+		memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
+	
+		if(obj->chunkId >= 0)
+		{
+			yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL,1);
+		}
+		strncpy(name,oh->name,buffSize - 1);
+	}
+	
+	return strlen(name);
+}
+
+int yaffs_GetObjectFileLength(yaffs_Object *obj)
+{
+	
+	// Dereference any hard linking
+	obj = yaffs_GetEquivalentObject(obj);
+	
+	if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
+	{
+		return obj->variant.fileVariant.fileSize;
+	}
+	if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
+	{
+		return strlen(obj->variant.symLinkVariant.alias);
+	}
+	else
+	{
+		// Only a directory should drop through to here
+		return YAFFS_BYTES_PER_CHUNK;
+	}	
+}
+
+int yaffs_GetObjectLinkCount(yaffs_Object *obj)
+{
+	int count = 0; 
+	struct list_head *i;
+	
+	if(!obj->unlinked)
+	{
+		count++;	// the object itself
+	}
+	list_for_each(i,&obj->hardLinks)
+	{
+		count++;	// add the hard links;
+	}
+	return count;
+	
+}
+
+
+int yaffs_GetObjectInode(yaffs_Object *obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	
+	return obj->objectId;
+}
+
+unsigned yaffs_GetObjectType(yaffs_Object *obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	
+	switch(obj->variantType)
+	{
+		case YAFFS_OBJECT_TYPE_FILE:		return DT_REG; break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY:	return DT_DIR; break;
+		case YAFFS_OBJECT_TYPE_SYMLINK:		return DT_LNK; break;
+		case YAFFS_OBJECT_TYPE_HARDLINK:	return DT_REG; break;
+		case YAFFS_OBJECT_TYPE_SPECIAL:		
+			if(S_ISFIFO(obj->yst_mode)) return DT_FIFO;
+			if(S_ISCHR(obj->yst_mode)) return DT_CHR;
+			if(S_ISBLK(obj->yst_mode)) return DT_BLK;
+			if(S_ISSOCK(obj->yst_mode)) return DT_SOCK;
+		default: return DT_REG; break;
+	}
+}
+
+char *yaffs_GetSymlinkAlias(yaffs_Object *obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
+	{
+		return yaffs_CloneString(obj->variant.symLinkVariant.alias);
+	}
+	else
+	{
+		return yaffs_CloneString("");
+	}
+}
+
+#ifndef CONFIG_YAFFS_WINCE
+
+int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)
+{
+	unsigned int valid = attr->ia_valid;
+	
+	if(valid & ATTR_MODE) obj->yst_mode = attr->ia_mode;
+	if(valid & ATTR_UID) obj->yst_uid = attr->ia_uid;
+	if(valid & ATTR_GID) obj->yst_gid = attr->ia_gid;
+	
+	if(valid & ATTR_ATIME) obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime);
+	if(valid & ATTR_CTIME) obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime);
+	if(valid & ATTR_MTIME) obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime);
+
+	
+	if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
+	
+	yaffs_UpdateObjectHeader(obj,NULL,1);
+	
+	return YAFFS_OK;
+	
+}
+
+int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
+{
+	unsigned int valid = 0;
+	
+	attr->ia_mode = obj->yst_mode;	valid |= ATTR_MODE;
+	attr->ia_uid = obj->yst_uid;		valid |= ATTR_UID;
+	attr->ia_gid = obj->yst_gid;		valid |= ATTR_GID;
+	
+	
+	Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime;	valid |= ATTR_ATIME;
+	Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime;	valid |= ATTR_CTIME;
+	Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime;	valid |= ATTR_MTIME;
+
+	attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
+	
+	attr->ia_valid = valid;
+	
+	return YAFFS_OK;
+	
+}
+
+#endif
+
+int yaffs_DumpObject(yaffs_Object *obj)
+{
+//	__u8 buffer[YAFFS_BYTES_PER_CHUNK];
+	char name[257];
+//	yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
+
+//	memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
+	
+//	if(obj->chunkId >= 0)
+//	{
+//		yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
+//	}
+	
+	yaffs_GetObjectName(obj,name,256);
+	
+	T(YAFFS_TRACE_ALWAYS,(TSTR("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n" TENDSTR),
+			obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial, 
+			obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
+
+#if 0
+	YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n",
+			obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial, 
+			obj->sum, obj->chunkId));
+		switch(obj->variantType)
+	{
+		case YAFFS_OBJECT_TYPE_FILE: 
+			YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize));
+			break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY:
+			YPRINTF((" DIRECTORY\n"));
+			break;
+		case YAFFS_OBJECT_TYPE_HARDLINK: //todo
+		case YAFFS_OBJECT_TYPE_SYMLINK:
+		case YAFFS_OBJECT_TYPE_UNKNOWN:
+		default:
+	}
+#endif
+	
+	return YAFFS_OK;
+}
+
+
+///////////////////////// Initialisation code ///////////////////////////
+
+
+
+int yaffs_GutsInitialise(yaffs_Device *dev)
+{
+	unsigned x;
+	int bits;
+	int extraBits;
+	int nBlocks;
+
+	if(	dev->nBytesPerChunk != YAFFS_BYTES_PER_CHUNK ||	
+		dev->nChunksPerBlock < 2 ||
+		dev->nReservedBlocks < 2 ||
+		dev->startBlock < 0 ||
+		dev->endBlock <= 0 ||
+		dev->endBlock <= (dev->startBlock + dev->nReservedBlocks)
+	  )
+	{
+		//these parameters must be set before stating yaffs
+		// Other parameters internalStartBlock,
+		return YAFFS_FAIL;
+	}
+
+
+	
+	if(!yaffs_CheckStructures())
+	{
+		T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs_CheckStructures failed\n" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	if(dev->isMounted)
+	{
+		T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: device already mounted\n" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	dev->isMounted = 1;
+
+	if(dev->startBlock < 0 ||
+	   (dev->endBlock - dev->startBlock) < 10)
+	{
+		T(YAFFS_TRACE_ALWAYS,(TSTR("startBlock %d or endBlock %d invalid\n" TENDSTR),
+				dev->startBlock, dev->endBlock));
+		return YAFFS_FAIL;
+	}
+	
+	
+	// Do we need to add an offset to use block 0?
+	
+	dev->internalStartBlock = dev->startBlock;
+	dev->internalEndBlock = dev->endBlock;
+	dev->blockOffset = 0;
+	dev->chunkOffset = 0;
+	
+	if(dev->startBlock == 0)
+	{
+		dev->internalStartBlock++;
+		dev->internalEndBlock++;
+		dev->blockOffset++;
+		dev->chunkOffset = dev->nChunksPerBlock;		
+	}
+	
+	
+	nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+	
+
+		
+	// OK now calculate a few things for the device
+	// Calculate chunkGroupBits. 
+	// We need to find the next power of 2 > than internalEndBlock
+	
+	x = dev->nChunksPerBlock * (dev->internalEndBlock+1);
+	
+	for(bits = extraBits = 0; x > 1; bits++)
+	{
+		if(x & 1) extraBits++;
+		x >>= 1;
+	}
+
+	if(extraBits > 0) bits++;
+	
+	
+	// Level0 Tnodes are 16 bits, so if the bitwidth of the
+	// chunk range we're using is greater than 16 we need 
+	// to figure out chunk shift and chunkGroupSize
+	if(bits <= 16) 
+	{
+		dev->chunkGroupBits = 0;
+	}
+	else
+	{
+		dev->chunkGroupBits = bits - 16;
+	}
+	
+	dev->chunkGroupSize = 1 << dev->chunkGroupBits;
+
+	if(dev->nChunksPerBlock < dev->chunkGroupSize)
+	{
+		// We have a problem because the soft delete won't work if
+		// the chunk group size > chunks per block.
+		// This can be remedied by using larger "virtual blocks".
+		
+		return YAFFS_FAIL;
+	}
+
+	
+	
+	
+	// More device initialisation
+	dev->garbageCollections = 0;
+	dev->passiveGarbageCollections = 0;
+	dev->currentDirtyChecker = 0;
+	dev->bufferedBlock = -1;
+	dev->doingBufferedBlockRewrite = 0;
+	dev->nDeletedFiles = 0;
+	dev->nBackgroundDeletions=0;
+	dev->nUnlinkedFiles = 0;
+	dev->eccFixed=0;
+	dev->eccUnfixed=0;
+	dev->tagsEccFixed=0;
+	dev->tagsEccUnfixed=0;
+	dev->nErasedBlocks=0;
+	
+	dev->localBuffer = YMALLOC(dev->nBytesPerChunk);
+	
+
+	
+	
+	yaffs_InitialiseBlocks(dev,nBlocks);
+	
+	yaffs_InitialiseTnodes(dev);
+
+	yaffs_InitialiseObjects(dev);
+	
+	if(dev->nShortOpCaches > 0)
+	{ 
+		int i;
+		
+		if(dev->nShortOpCaches >  YAFFS_MAX_SHORT_OP_CACHES)
+		{
+			dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES;
+		}
+		
+		dev->srCache = YMALLOC( dev->nShortOpCaches * sizeof(yaffs_ChunkCache));
+		
+		for(i=0; i < dev->nShortOpCaches; i++)
+		{
+			dev->srCache[i].object = NULL;
+			dev->srCache[i].lastUse = 0;
+			dev->srCache[i].dirty = 0;
+		}
+		dev->srLastUse = 0;
+	}
+
+	dev->cacheHits = 0;
+	
+	
+	// Initialise the unlinked, root and lost and found directories
+	dev->lostNFoundDir = dev->rootDir = dev->unlinkedDir = NULL;
+	
+	dev->unlinkedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR);
+
+	dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);
+	dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_LOSTNFOUND_MODE | S_IFDIR);
+	yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);
+	
+		
+	// Now scan the flash.	
+	yaffs_Scan(dev);
+	
+	// Zero out stats
+	dev->nPageReads = 0;
+    dev->nPageWrites =  0;
+	dev->nBlockErasures = 0;
+	dev->nGCCopies = 0;
+	dev->nRetriedWrites = 0;
+	dev->nRetiredBlocks = 0;
+
+	
+	return YAFFS_OK;
+		
+}
+
+void yaffs_Deinitialise(yaffs_Device *dev)
+{
+	if(dev->isMounted)
+	{
+	
+		yaffs_DeinitialiseBlocks(dev);
+		yaffs_DeinitialiseTnodes(dev);
+		yaffs_DeinitialiseObjects(dev);
+		if(dev->nShortOpCaches > 0)
+			YFREE(dev->srCache);
+		YFREE(dev->localBuffer);
+		dev->isMounted = 0;
+	}
+	
+}
+
+#if 0
+
+int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+	int nFree = dev->nFreeChunks - (dev->nChunksPerBlock * YAFFS_RESERVED_BLOCKS);
+	
+	struct list_head *i;	
+	yaffs_Object *l;
+	
+	
+	// To the free chunks add the chunks that are in the deleted unlinked files.
+	list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+	{
+		l = list_entry(i, yaffs_Object,siblings);
+		if(l->deleted)
+		{
+			nFree++;
+			nFree += l->nDataChunks;
+		}
+	}
+	
+	
+	// printf("___________ nFreeChunks is %d nFree is %d\n",dev->nFreeChunks,nFree);	
+
+	if(nFree < 0) nFree = 0;
+
+	return nFree;	
+	
+}
+
+#endif
+
+int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+	int nFree;
+	int pending;
+	int b;
+	int nDirtyCacheChunks=0;
+	
+	yaffs_BlockInfo *blk;
+	
+	struct list_head *i;	
+	yaffs_Object *l;
+	
+	for(nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock; b++)
+	{
+		blk = yaffs_GetBlockInfo(dev,b);
+		
+		switch(blk->blockState)
+		{
+			case YAFFS_BLOCK_STATE_EMPTY:
+			case YAFFS_BLOCK_STATE_ALLOCATING: 
+			case YAFFS_BLOCK_STATE_FULL: nFree += (dev->nChunksPerBlock - blk->pagesInUse); break;
+			default: break;
+		}
+	}
+	
+	
+	pending = 0;
+	
+	// To the free chunks add the chunks that are in the deleted unlinked files.
+	list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+	{
+		if(i)
+		{
+			l = list_entry(i, yaffs_Object,siblings);
+			if(l->deleted)
+			{
+				pending++;
+				pending += l->nDataChunks;
+			}
+		}
+	}
+	
+	
+	
+	//printf("___________ really free is %d, pending %d, nFree is %d\n",nFree,pending, nFree+pending);
+	
+	if(nFree != dev->nFreeChunks) 
+	{
+	//	printf("___________Different! really free is %d, nFreeChunks %d\n",nFree dev->nFreeChunks);
+	}
+
+	nFree += pending;
+	
+	// Now count the number of dirty chunks in the cache and subtract those
+	
+	{
+		int i;
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(dev->srCache[i].dirty) nDirtyCacheChunks++;
+		}
+	}
+	
+	nFree -= nDirtyCacheChunks;
+	
+	nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock);
+	
+	if(nFree < 0) nFree = 0;
+
+	return nFree;	
+	
+}
+
+
+
+/////////////////// YAFFS test code //////////////////////////////////
+
+#define yaffs_CheckStruct(structure,syze, name) \
+           if(sizeof(structure) != syze) \
+	       { \
+	         T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),name,syze,sizeof(structure))); \
+	         return YAFFS_FAIL; \
+		   }
+		 
+		 
+static int yaffs_CheckStructures(void)
+{
+	yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")
+	yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")
+	yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")
+#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG
+	yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")
+#endif
+	yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")
+	
+	
+	return YAFFS_OK;
+}
+
+#if 0
+void yaffs_GutsTest(yaffs_Device *dev)
+{
+	
+	if(yaffs_CheckStructures() != YAFFS_OK)
+	{
+		T(YAFFS_TRACE_ALWAYS,(TSTR("One or more structures malformed-- aborting\n" TENDSTR)));
+		return;
+	}
+	
+	yaffs_TnodeTest(dev);
+	yaffs_ObjectTest(dev);	
+}
+#endif
+
+
+
+
+
+
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_guts.h linux-2.4.30/fs/yaffs/yaffs_guts.h
--- linux-2.4.30-orig/fs/yaffs/yaffs_guts.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_guts.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,598 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yaffs_guts.h: Configuration etc for yaffs_guts
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * $Id: yaffs_guts.h,v 1.1 2005/10/13 15:18:47 lel Exp $
+ */
+
+#ifndef __YAFFS_GUTS_H__
+#define __YAFFS_GUTS_H__
+
+#include "yportenv.h"
+
+#define YAFFS_OK	1
+#define YAFFS_FAIL  0
+
+// Give us a Y=0x59, 
+// Give us an A=0x41, 
+// Give us an FF=0xFF 
+// Give us an S=0x53
+// And what have we got... 
+#define YAFFS_MAGIC					0x5941FF53
+
+#define YAFFS_NTNODES_LEVEL0	  	16
+#define YAFFS_TNODES_LEVEL0_BITS	4
+#define YAFFS_TNODES_LEVEL0_MASK	0xf
+
+#define YAFFS_NTNODES_INTERNAL 		(YAFFS_NTNODES_LEVEL0 / 2)
+#define YAFFS_TNODES_INTERNAL_BITS 	(YAFFS_TNODES_LEVEL0_BITS - 1)
+#define YAFFS_TNODES_INTERNAL_MASK	0x7
+#define YAFFS_TNODES_MAX_LEVEL		6
+		
+#define YAFFS_BYTES_PER_SPARE		16
+
+#define YAFFS_BYTES_PER_CHUNK		512
+//#define YAFFS_CHUNK_SIZE_SHIFT		9
+
+
+#define YAFFS_CHUNKS_PER_BLOCK		32
+#define YAFFS_BYTES_PER_BLOCK		(YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
+
+#define YAFFS_MAX_CHUNK_ID			0x000FFFFF
+
+#define YAFFS_UNUSED_OBJECT_ID		0x0003FFFF
+
+#define YAFFS_ALLOCATION_NOBJECTS	100
+#define YAFFS_ALLOCATION_NTNODES	100
+#define YAFFS_ALLOCATION_NLINKS		100
+
+#define YAFFS_NOBJECT_BUCKETS		256
+
+
+#define YAFFS_OBJECT_SPACE			0x40000
+#define YAFFS_MAX_NAME_LENGTH		255
+#define YAFFS_SHORT_NAME_LENGTH		15
+
+#define YAFFS_MAX_ALIAS_LENGTH		159
+
+#define YAFFS_OBJECTID_ROOT			1
+#define YAFFS_OBJECTID_LOSTNFOUND	2
+#define YAFFS_OBJECTID_UNLINKED		3
+
+#define YAFFS_MAX_SHORT_OP_CACHES	20
+
+
+// ChunkCache is used for short read/write operations.
+typedef struct
+{
+	struct yaffs_ObjectStruct *object;
+	int chunkId;
+	int lastUse;
+	int dirty;	
+	int nBytes;	// Only valid if the cache is dirty
+	__u8 data[YAFFS_BYTES_PER_CHUNK];
+} yaffs_ChunkCache;
+
+// Tags structures in RAM
+// NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise
+// the structure size will get blown out.
+
+typedef struct
+{
+    unsigned chunkId:20;
+    unsigned serialNumber:2;
+    unsigned byteCount:10;
+    unsigned objectId:18;
+    unsigned ecc:12;
+    unsigned unusedStuff:2;
+} yaffs_Tags;
+
+typedef union
+{
+    yaffs_Tags asTags;
+    __u8       asBytes[8];
+} yaffs_TagsUnion;
+
+
+// Spare structure
+typedef struct
+{
+    __u8  tagByte0;
+    __u8  tagByte1;
+    __u8  tagByte2;
+    __u8  tagByte3;
+    __u8  pageStatus; 	// set to 0 to delete the chunk
+    __u8  blockStatus;
+    __u8  tagByte4;
+    __u8  tagByte5;
+    __u8  ecc1[3];
+    __u8  tagByte6;
+    __u8  tagByte7;
+    __u8  ecc2[3];
+} yaffs_Spare;
+
+//Special structure for passing through to mtd
+struct yaffs_NANDSpare {
+	yaffs_Spare	spare;
+	int		eccres1;
+	int		eccres2;
+};
+
+// Block data in RAM
+
+typedef enum {
+	YAFFS_BLOCK_STATE_UNKNOWN	= 0,
+	YAFFS_BLOCK_STATE_SCANNING,		// Used while the block is being scanned.
+									// NB Don't erase blocks while they're being scanned
+	
+	YAFFS_BLOCK_STATE_EMPTY,		// This block is empty
+	
+	YAFFS_BLOCK_STATE_ALLOCATING,	// This block is partially allocated. 
+									// This is the one currently being used for page
+									// allocation. Should never be more than one of these
+							
+
+	YAFFS_BLOCK_STATE_FULL,			// All the pages in this block have been allocated.
+									// At least one page holds valid data.
+							 
+	YAFFS_BLOCK_STATE_DIRTY,		// All pages have been allocated and deleted. 
+									// Erase me, reuse me.
+							
+	YAFFS_BLOCK_STATE_DEAD 			// This block has failed and is not in use
+
+} yaffs_BlockState;
+
+
+
+
+typedef struct
+{
+#ifndef CONFIG_YAFFS_NO_YAFFS2
+	__u32 sequenceNumber;	// block sequence number for yaffs2
+#endif
+	int   softDeletions:12; // number of soft deleted pages
+    int   pagesInUse:12;	// number of pages in use
+    __u32 blockState:4; 	// One of the above block states
+    __u32 needsRetiring:1;	// Data has failed on this block, need to get valid data off
+    						// and retire the block.
+} yaffs_BlockInfo;
+
+
+//////////////////// Object structure ///////////////////////////
+// This is the object structure as stored on NAND
+
+typedef enum
+{
+	YAFFS_OBJECT_TYPE_UNKNOWN,
+	YAFFS_OBJECT_TYPE_FILE,
+	YAFFS_OBJECT_TYPE_SYMLINK,
+	YAFFS_OBJECT_TYPE_DIRECTORY,
+	YAFFS_OBJECT_TYPE_HARDLINK,
+	YAFFS_OBJECT_TYPE_SPECIAL
+} yaffs_ObjectType;
+
+typedef struct
+{
+	yaffs_ObjectType type;
+
+	// Apply to everything	
+	int   parentObjectId;
+	__u16 sum__NoLongerUsed;	// checksum of name. Calc this off the name to prevent inconsistencies
+	char  name[YAFFS_MAX_NAME_LENGTH + 1];
+
+	// Thes following apply to directories, files, symlinks - not hard links
+	__u32 yst_mode;  // protection
+
+#ifdef CONFIG_YAFFS_WINCE
+	__u32 notForWinCE[5];
+#else
+	__u32 yst_uid;   // user ID of owner
+	__u32 yst_gid;    // group ID of owner 
+	__u32 yst_atime; // time of last access
+	__u32 yst_mtime; // time of last modification
+	__u32 yst_ctime; // time of last change
+#endif
+
+	// File size  applies to files only
+	int fileSize; 
+		
+	// Equivalent object id applies to hard links only.
+	int  equivalentObjectId;
+	
+	// Alias is for symlinks only.
+	char alias[YAFFS_MAX_ALIAS_LENGTH + 1];
+	
+	__u32 yst_rdev;  // device stuff for block and char devices (maj/min)
+	
+#ifdef CONFIG_YAFFS_WINCE
+	__u32 win_ctime[2];
+	__u32 win_atime[2];
+	__u32 win_mtime[2];
+	__u32 roomToGrow[6];
+#else
+	__u32 roomToGrow[12];
+#endif
+	
+} yaffs_ObjectHeader;
+
+
+
+////////////////////  Tnode ///////////////////////////
+
+union yaffs_Tnode_union
+{
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+	union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL+1];
+#else
+	union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL];
+#endif
+	__u16 level0[YAFFS_NTNODES_LEVEL0];
+	
+};
+
+typedef union yaffs_Tnode_union yaffs_Tnode;
+
+struct yaffs_TnodeList_struct
+{
+	struct yaffs_TnodeList_struct *next;
+	yaffs_Tnode *tnodes;
+};
+
+typedef struct yaffs_TnodeList_struct yaffs_TnodeList;
+
+
+
+///////////////////  Object ////////////////////////////////
+// An object can be one of:
+// - a directory (no data, has children links
+// - a regular file (data.... not prunes :->).
+// - a symlink [symbolic link] (the alias).
+// - a hard link
+
+
+typedef struct 
+{
+	__u32 fileSize;
+	__u32 scannedFileSize;
+	int   topLevel;
+	yaffs_Tnode *top;
+} yaffs_FileStructure;
+
+typedef struct
+{
+	struct list_head children; // list of child links
+} yaffs_DirectoryStructure;
+
+typedef struct
+{
+	char *alias;
+} yaffs_SymLinkStructure;
+
+typedef struct
+{
+	struct yaffs_ObjectStruct *equivalentObject;
+	__u32	equivalentObjectId;
+} yaffs_HardLinkStructure;
+
+typedef union
+{
+	yaffs_FileStructure fileVariant;
+	yaffs_DirectoryStructure directoryVariant;
+	yaffs_SymLinkStructure symLinkVariant;
+	yaffs_HardLinkStructure hardLinkVariant;
+} yaffs_ObjectVariant;
+
+
+struct  yaffs_ObjectStruct
+{
+	__u8 deleted: 1;		// This should only apply to unlinked files.
+	__u8 softDeleted: 1;	// it has also been soft deleted
+	__u8 unlinked: 1;		// An unlinked file. The file should be in the unlinked pseudo directory.
+	__u8 fake:1;			// A fake object has no presence on NAND.
+	__u8 renameAllowed:1;
+	__u8 unlinkAllowed:1;
+	__u8 dirty:1;			// the object needs to be written to flash
+	__u8 valid:1;			// When the file system is being loaded up, this 
+							// object might be created before the data
+							// is available (ie. file data records appear before the header).
+	__u8 serial;			// serial number of chunk in NAND. Store here so we don't have to
+							// read back the old one to update.
+	__u16 sum;				// sum of the name to speed searching
+	
+	struct yaffs_DeviceStruct *myDev; // The device I'm on
+	
+								
+	struct list_head hashLink;	// list of objects in this hash bucket
+							
+
+	struct list_head hardLinks; // all the equivalent hard linked objects
+								// live on this list
+	// directory structure stuff
+	struct yaffs_ObjectStruct  *parent;	//my parent directory
+	struct list_head siblings;	// siblings in a directory
+								// also used for linking up the free list
+		
+	// Where's my data in NAND?
+	int chunkId;		// where it lives
+
+	int nDataChunks;	
+	
+	__u32 objectId;		// the object id value
+	
+	
+	__u32 yst_mode;  	// protection
+
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+	char shortName[YAFFS_SHORT_NAME_LENGTH+1];
+#endif
+
+#ifndef __KERNEL__
+	__u32 inUse;
+#endif
+
+#ifdef CONFIG_YAFFS_WINCE
+	__u32 win_ctime[2];
+	__u32 win_mtime[2];
+	__u32 win_atime[2];
+#else
+	__u32 yst_uid;   	// user ID of owner
+	__u32 yst_gid;    	// group ID of owner 
+	__u32 yst_atime; 	// time of last access
+	__u32 yst_mtime; 	// time of last modification
+	__u32 yst_ctime; 	// time of last change
+#endif
+
+	__u32 yst_rdev; 	    // device stuff for block and char devices
+
+
+
+#ifdef __KERNEL__
+	struct inode *myInode;
+	__u8  deferedFree;   // YAFFS has removed the object from NAND, but it is being kept
+			     // Alive until the inode is cleared to prevent inode inconsistencies.
+#endif
+
+
+	
+	yaffs_ObjectType variantType;
+	
+	yaffs_ObjectVariant variant;
+	
+};
+
+
+
+typedef struct yaffs_ObjectStruct yaffs_Object;
+
+
+struct yaffs_ObjectList_struct
+{
+	yaffs_Object *objects;
+	struct yaffs_ObjectList_struct *next;
+};
+
+typedef struct yaffs_ObjectList_struct yaffs_ObjectList;
+
+typedef struct
+{
+	struct list_head list;
+	int count;
+} yaffs_ObjectBucket;
+
+
+//////////////////// Device ////////////////////////////////
+
+struct yaffs_DeviceStruct
+{
+	// Entry parameters set up way early. Yaffs sets up the rest.
+	int   nBytesPerChunk; 	 // Should be a power of 2 >= 512
+	int   nChunksPerBlock;	 // does not need to be a power of 2
+	int   startBlock;		 // Start block we're allowed to use
+	int   endBlock;			 // End block we're allowed to use
+	int   nReservedBlocks;	 // We want this tuneable so that we can reduce
+							 // reserved blocks on NOR and RAM.
+	
+	int   useNANDECC;		// Flag to decide whether or not to use NANDECC
+	int   nShortOpCaches;	// If <= 0, then short op caching is disabled, else
+							// the number of short op caches (don't use too many).
+	
+	
+	void *genericDevice; // Pointer to device context
+						 // On an mtd this holds the mtd pointer.
+
+	// NAND access functions (Must be set before calling YAFFS)
+	
+	int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare);
+	int (*readChunkFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
+	int (*eraseBlockInNAND)(struct yaffs_DeviceStruct *dev,int blockInNAND);	
+	int (*initialiseNAND)(struct yaffs_DeviceStruct *dev);
+
+	// Runtime parameters. Set up by YAFFS.
+	int   internalStartBlock;		 // Internal version of startBlock
+	int   internalEndBlock;			 // End block we're allowed to use
+	int   blockOffset;
+	int   chunkOffset;
+	
+	
+	__u16 chunkGroupBits; // 0 for devices <= 32MB. else log2(nchunks) - 16
+	__u16 chunkGroupSize; // == 2^^chunkGroupBits
+	
+#ifdef __KERNEL__
+
+	struct semaphore sem;// Semaphore for waiting on erasure.
+	struct semaphore grossLock; // Gross locking semaphore
+
+#endif	
+#ifdef __KERNEL__
+	void (*putSuperFunc)(struct super_block *sb);
+#endif
+
+	int isMounted;
+	
+	// Block Info
+	yaffs_BlockInfo *blockInfo;
+	__u8 *chunkBits;   // bitmap of chunks in use
+	int   chunkBitmapStride; // Number of bytes of chunkBits per block. 
+							 //	Must be consistent with nChunksPerBlock.
+
+
+	int   nErasedBlocks;
+	int   allocationBlock;			// Current block being allocated off
+	__u32 allocationPage;
+	int   allocationBlockFinder;	// Used to search for next allocation block
+	
+	// Runtime state
+	int   nTnodesCreated;	
+	yaffs_Tnode *freeTnodes;
+	int  nFreeTnodes;
+	yaffs_TnodeList *allocatedTnodeList;
+
+
+	int   nObjectsCreated;
+	yaffs_Object *freeObjects;
+	int   nFreeObjects;
+
+	yaffs_ObjectList *allocatedObjectList;
+
+	yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS];
+
+	int	  nFreeChunks;
+		
+	int   currentDirtyChecker;	// Used to find current dirtiest block
+	
+	
+	// Operations since mount
+	int nPageWrites;
+	int nPageReads;
+	int nBlockErasures;
+	int nGCCopies;
+	int garbageCollections;
+	int passiveGarbageCollections;
+	int nRetriedWrites;
+	int nRetiredBlocks;
+	int eccFixed;
+	int eccUnfixed;
+	int tagsEccFixed;
+	int tagsEccUnfixed;
+	int nDeletions;
+	int nUnmarkedDeletions;
+	
+	yaffs_Object *rootDir;
+	yaffs_Object *lostNFoundDir;
+	
+	// Buffer areas for storing data to recover from write failures
+//	__u8 		bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK];
+//	yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK];
+	int bufferedBlock;	// Which block is buffered here?
+	int doingBufferedBlockRewrite;
+
+	yaffs_ChunkCache *srCache;
+	int srLastUse;
+
+	int cacheHits;
+
+	// Stuff for background deletion and unlinked files.
+	yaffs_Object *unlinkedDir;		// Directory where unlinked and deleted files live.
+	yaffs_Object *unlinkedDeletion;	// Current file being background deleted.
+	int nDeletedFiles;				// Count of files awaiting deletion;
+	int nUnlinkedFiles;				// Count of unlinked files. 
+	int nBackgroundDeletions;			// Count of background deletions.	
+	
+	__u8 *localBuffer;
+	
+};
+
+typedef struct yaffs_DeviceStruct yaffs_Device;
+
+
+
+//////////// YAFFS Functions //////////////////
+
+int yaffs_GutsInitialise(yaffs_Device *dev);
+void yaffs_Deinitialise(yaffs_Device *dev);
+
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev);
+
+
+// Rename
+int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName);
+
+// generic Object functions
+int yaffs_Unlink(yaffs_Object *dir, const char *name);
+int yaffs_DeleteFile(yaffs_Object *obj);
+
+// Object access functions.
+int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize);
+int yaffs_GetObjectFileLength(yaffs_Object *obj);
+int yaffs_GetObjectInode(yaffs_Object *obj);
+unsigned yaffs_GetObjectType(yaffs_Object *obj);
+int yaffs_GetObjectLinkCount(yaffs_Object *obj);
+
+// Change inode attributes
+int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr);
+int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr);
+
+// File operations
+int yaffs_ReadDataFromFile(yaffs_Object *obj, __u8 *buffer, __u32 offset, int nBytes);
+int yaffs_WriteDataToFile(yaffs_Object *obj, const __u8 *buffer, __u32 offset, int nBytes);
+int yaffs_ResizeFile(yaffs_Object *obj, int newSize);
+
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid);
+int yaffs_FlushFile(yaffs_Object *obj,int updateTime);
+
+
+// Directory operations
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid);
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *theDir,const char *name);
+int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *));
+
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number);
+
+// Link operations
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject);
+
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj);
+
+// Symlink operations
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent, const char *name, __u32 mode,  __u32 uid, __u32 gid, const char *alias);
+char *yaffs_GetSymlinkAlias(yaffs_Object *obj);
+
+// Special inodes (fifos, sockets and devices)
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,__u32 rdev);
+
+
+// Special directories
+yaffs_Object *yaffs_Root(yaffs_Device *dev);
+yaffs_Object *yaffs_LostNFound(yaffs_Device *dev);
+
+#ifdef CONFIG_YAFFS_WINCE
+// CONFIG_YAFFS_WINCE special stuff
+void  yfsd_WinFileTimeNow(__u32 target[2]);
+#endif
+
+#ifdef __KERNEL__
+void yaffs_HandleDeferedFree(yaffs_Object *obj);
+#endif
+
+// Debug dump 
+int yaffs_DumpObject(yaffs_Object *obj);
+
+
+void yaffs_GutsTest(yaffs_Device *dev);
+
+
+#endif
+
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_mtdif.c linux-2.4.30/fs/yaffs/yaffs_mtdif.c
--- linux-2.4.30-orig/fs/yaffs/yaffs_mtdif.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_mtdif.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,169 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yaffs_mtdif.c  NAND mtd wrapper functions.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * 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.
+ *
+ */
+
+const char *yaffs_mtdif_c_version = "$Id: yaffs_mtdif.c,v 1.1 2005/10/13 15:18:47 lel Exp $";
+
+#include <linux/config.h>
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+ 
+#include "yportenv.h"
+
+#include "yaffs_mtdif.h"
+
+#include "linux/mtd/mtd.h"
+#include "linux/types.h"
+#include "linux/time.h"
+
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+#include "linux/mtd/nand.h"
+#endif
+
+struct nand_oobinfo yaffs_oobinfo = {
+	useecc: 1,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8))
+// this is for versions of mtd nand driver in kernel 2.6.8 and later
+	eccbytes: 6,
+#endif
+	eccpos: {8, 9, 10, 13, 14, 15}
+};
+
+struct nand_oobinfo yaffs_noeccinfo = {
+	useecc: 0,
+};
+
+
+int nandmtd_WriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_Spare *spare)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	size_t dummy;
+    int retval = 0;
+	
+	loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
+	
+	__u8 *spareAsBytes = (__u8 *)spare;
+
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+	if(data && spare)
+	{
+		if(dev->useNANDECC)
+			mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_oobinfo);
+		else
+			mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_noeccinfo);
+	}
+	else
+	{
+#endif
+	if(data)
+		retval = mtd->write(mtd,addr,dev->nBytesPerChunk,&dummy,data);
+	if(spare)
+		retval = mtd->write_oob(mtd,addr,YAFFS_BYTES_PER_SPARE,&dummy,spareAsBytes);
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+	}
+#endif
+
+    if (retval == 0)
+    	return YAFFS_OK;
+    else
+        return YAFFS_FAIL;
+}
+
+int nandmtd_ReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	size_t dummy;
+    int retval = 0;
+	
+	loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
+	
+	__u8 *spareAsBytes = (__u8 *)spare;
+	
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+	if(data && spare)
+	{
+		if(dev->useNANDECC)
+		{   // Careful, this call adds 2 ints to the end of the spare data.  Calling function should
+            // allocate enough memory for spare, i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)].
+    		retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_oobinfo);            
+		}
+		else
+		{
+			retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_noeccinfo);
+		}
+	}
+	else
+	{
+#endif
+	if(data)
+		retval = mtd->read(mtd,addr,dev->nBytesPerChunk,&dummy,data);
+	if(spare)
+		retval = mtd->read_oob(mtd,addr,YAFFS_BYTES_PER_SPARE,&dummy,spareAsBytes);
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+	}
+#endif
+
+    if (retval == 0)
+    	return YAFFS_OK;
+    else
+        return YAFFS_FAIL;
+}
+
+// Callback not needed for NAND
+#if 0
+static void nandmtd_EraseCallback(struct erase_info *ei)
+{
+	yaffs_Device *dev = (yaffs_Device *)ei->priv;	
+	up(&dev->sem);
+}
+#endif
+
+
+int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	__u32 addr = ((loff_t) blockNumber) * dev->nBytesPerChunk * dev->nChunksPerBlock;
+	struct erase_info ei;
+    int retval = 0;
+	
+	ei.mtd = mtd;
+	ei.addr = addr;
+	ei.len = dev->nBytesPerChunk * dev->nChunksPerBlock;
+	ei.time = 1000;
+	ei.retries = 2;
+	ei.callback = NULL;
+	ei.priv = (u_long)dev;
+	
+	// Todo finish off the ei if required
+	
+	sema_init(&dev->sem,0);
+
+	retval = mtd->erase(mtd,&ei);	
+	
+	//No need for callback 
+	// down(&dev->sem); // Wait for the erasure to complete
+
+    if (retval == 0)	
+    	return YAFFS_OK;
+    else
+        return YAFFS_FAIL;
+}
+
+int nandmtd_InitialiseNAND(yaffs_Device *dev)
+{
+	return YAFFS_OK;
+}
+
+#endif // CONFIG_YAFFS_MTD_ENABLED
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_mtdif.h linux-2.4.30/fs/yaffs/yaffs_mtdif.h
--- linux-2.4.30-orig/fs/yaffs/yaffs_mtdif.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_mtdif.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,33 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yaffs_mtdif.h  NAND mtd interface wrappers
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * $Id: yaffs_mtdif.h,v 1.1 2005/10/13 15:18:47 lel Exp $
+ */
+
+#ifndef __YAFFS_MTDIF_H__
+#define __YAFFS_MTDIF_H__
+
+#include "yaffs_guts.h"
+
+int nandmtd_WriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_Spare *spare);
+int nandmtd_ReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
+int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber);
+int nandmtd_InitialiseNAND(yaffs_Device *dev);
+#endif
+
+
+
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_nandemul.h linux-2.4.30/fs/yaffs/yaffs_nandemul.h
--- linux-2.4.30-orig/fs/yaffs/yaffs_nandemul.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_nandemul.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,38 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * yaffs_nandemul.h: Interface to emulated NAND functions
+ *
+ * $Id: yaffs_nandemul.h,v 1.1 2005/10/13 15:18:47 lel Exp $
+ */
+ 
+#ifndef __YAFFS_NANDEMUL_H__
+#define __YAFFS_NANDEMUL_H__
+
+#include "yaffs_guts.h" 
+ 
+ 
+/* WriteChunkToNAND and ReadChunkFromNAND are used with two pointers. 
+ * If either of these pointers are null, then that field will not be 
+ * transferred.
+ */
+ 
+int nandemul_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare);
+int nandemul_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
+int nandemul_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND);
+int nandemul_InitialiseNAND(struct yaffs_DeviceStruct *dev);
+
+#endif
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffs_ramem.c linux-2.4.30/fs/yaffs/yaffs_ramem.c
--- linux-2.4.30-orig/fs/yaffs/yaffs_ramem.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffs_ramem.c	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,312 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yaffs_ramem.c  NAND emulation on top of a chunk of RAM
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * 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.
+ *
+ */
+ //yaffs_ramem.c
+ // Since this creates the RAM block at start up it is pretty useless for testing the scanner.
+
+const char *yaffs_ramem_c_version = "$Id: yaffs_ramem.c,v 1.1 2005/10/13 15:18:48 lel Exp $";
+
+#ifndef __KERNEL__
+#define CONFIG_YAFFS_RAM_ENABLED
+#endif
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+
+#include "yportenv.h"
+
+#include "yaffs_nandemul.h"
+#include "yaffs_guts.h"
+#include "yaffsinterface.h"
+#include "devextras.h"
+
+
+#define EM_SIZE_IN_MEG 2
+
+#define BLOCK_SIZE (32 * 528)
+#define BLOCKS_PER_MEG ((1024*1024)/(32 * 512))
+#define FILE_SIZE_IN_BLOCKS (FILE_SIZE_IN_MEG * BLOCKS_PER_MEG)
+#define FILE_SIZE_IN_BYTES (FILE_SIZE_IN_BLOCKS * BLOCK_SIZE)
+
+
+
+#define DEFAULT_SIZE_IN_MB 2
+
+typedef struct 
+{
+	__u8 data[528]; // Data + spare
+	int count[3];   // The programming count for each area of
+			// the page (0..255,256..511,512..527
+	int empty;      // is this empty?
+} nandemul_Page;
+
+typedef struct
+{
+	nandemul_Page page[32]; // The pages in the block
+	__u8 damaged; 		// Is the block damaged?
+	
+} nandemul_Block;
+
+
+
+typedef struct
+{
+	nandemul_Block **block;
+	int nBlocks;
+} nandemul_Device;
+
+static nandemul_Device ned;
+
+int sizeInMB = DEFAULT_SIZE_IN_MB;
+
+
+static void nandemul_yield(int n)
+{
+#ifdef __KERNEL__
+	if(n > 0) schedule_timeout(n);
+#endif
+
+}
+
+
+static void nandemul_ReallyEraseBlock(int blockNumber)
+{
+	int i;
+	
+	nandemul_Block *theBlock = ned.block[blockNumber];
+	
+	for(i = 0; i < 32; i++)
+	{
+		memset(theBlock->page[i].data,0xff,528);
+		theBlock->page[i].count[0] = 0;
+		theBlock->page[i].count[1] = 0;
+		theBlock->page[i].count[2] = 0;
+		theBlock->page[i].empty = 1;
+		nandemul_yield(2);
+	}
+
+}
+
+
+int nandemul_CalcNBlocks(void)
+{
+        switch(sizeInMB)
+        {
+        	case 8:
+        	case 16:
+        	case 32:
+        	case 64:
+        	case 128:
+        	case 256:
+        	case 512:
+        		break;
+        	default:
+        		sizeInMB = DEFAULT_SIZE_IN_MB;
+        }
+	return sizeInMB * 64;
+}
+
+
+
+static int  CheckInit(void)
+{
+	static int initialised = 0;
+	
+	int i;
+	int fail = 0;
+	int nBlocks; 
+	int nAllocated = 0;
+	
+	if(initialised) 
+	{
+		return YAFFS_OK;
+	}
+	
+	
+	nBlocks = nandemul_CalcNBlocks();
+	
+	ned.block = YMALLOC(sizeof(nandemul_Block *) * nBlocks);
+	
+	if(!ned.block) return 0;
+	
+	for(i=0; i <nBlocks; i++)
+	{
+		ned.block[i] = NULL;
+	}
+	
+	for(i=0; i <nBlocks && !fail; i++)
+	{
+		if((ned.block[i] = YMALLOC(sizeof(nandemul_Block))) == 0)
+		{
+			fail = 1;
+		}
+		else
+		{
+			nandemul_ReallyEraseBlock(i);
+			ned.block[i]->damaged = 0;
+			nAllocated++;
+		}
+	}
+	
+	if(fail)
+	{
+		for(i = 0; i < nAllocated; i++)
+		{
+			YFREE(ned.block[i]);
+		}
+		YFREE(ned.block);
+		
+		T(YAFFS_TRACE_ALWAYS,("Allocation failed, could only allocate %dMB of %dMB requested.\n",
+		   nAllocated/64,sizeInMB));
+		return 0;
+	}
+	
+	ned.nBlocks = nBlocks;
+	
+	initialised = 1;
+	
+	return 1;
+}
+
+int nandemul_WriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_Spare *spare)
+{
+	int blk;
+	int pg;
+	int i;
+	
+	__u8 *x;
+	
+	__u8 *spareAsBytes = (__u8 *)spare;
+
+	
+	CheckInit();
+	
+	blk = chunkInNAND/32;
+	pg = chunkInNAND%32;
+	
+	
+	if(data)
+	{
+		x = ned.block[blk]->page[pg].data;
+		
+		for(i = 0; i < 512; i++)
+		{
+			x[i] &=data[i];
+		}
+		
+		ned.block[blk]->page[pg].count[0]++;
+		ned.block[blk]->page[pg].count[1]++;
+		ned.block[blk]->page[pg].empty = 0;
+	}
+	
+	
+	if(spare)
+	{
+		x = &ned.block[blk]->page[pg].data[512];
+			
+		for(i = 0; i < 16; i++)
+		{
+			x[i] &=spareAsBytes[i];
+		}
+		ned.block[blk]->page[pg].count[2]++;
+	}
+	
+	if(spare || data)
+	{
+		nandemul_yield(1);
+	}
+
+	return YAFFS_OK;
+}
+
+
+int nandemul_ReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
+{
+	int blk;
+	int pg;
+
+	
+	CheckInit();
+	
+	blk = chunkInNAND/32;
+	pg = chunkInNAND%32;
+	
+	
+	if(data)
+	{
+		memcpy(data,ned.block[blk]->page[pg].data,512);
+	}
+	
+	
+	if(spare)
+	{
+		memcpy(spare,&ned.block[blk]->page[pg].data[512],16);
+	}
+
+	return YAFFS_OK;
+}
+
+
+int nandemul_CheckChunkErased(yaffs_Device *dev,int chunkInNAND)
+{
+	int blk;
+	int pg;
+	int i;
+
+	
+	CheckInit();
+	
+	blk = chunkInNAND/32;
+	pg = chunkInNAND%32;
+	
+	
+	for(i = 0; i < 528; i++)
+	{
+		if(ned.block[blk]->page[pg].data[i] != 0xFF)
+		{
+			return YAFFS_FAIL;
+		}
+	}
+
+	return YAFFS_OK;
+
+}
+
+int nandemul_EraseBlockInNAND(yaffs_Device *dev, int blockNumber)
+{
+	
+	CheckInit();
+	
+	if(blockNumber < 0 || blockNumber >= ned.nBlocks)
+	{
+		T(YAFFS_TRACE_ALWAYS,("Attempt to erase non-existant block %d\n",blockNumber));
+	}
+	else if(ned.block[blockNumber]->damaged)
+	{
+		T(YAFFS_TRACE_ALWAYS,("Attempt to erase damaged block %d\n",blockNumber));
+	}
+	else
+	{
+		nandemul_ReallyEraseBlock(blockNumber);
+	}
+	
+	return YAFFS_OK;
+}
+
+int nandemul_InitialiseNAND(yaffs_Device *dev)
+{
+	return YAFFS_OK;
+}
+
+#endif //YAFFS_RAM_ENABLED
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yaffsinterface.h linux-2.4.30/fs/yaffs/yaffsinterface.h
--- linux-2.4.30-orig/fs/yaffs/yaffsinterface.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yaffsinterface.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,25 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * yaffsinterface.h: Interface to the guts of yaffs.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ */
+ 
+#ifndef __YAFFSINTERFACE_H__
+#define __YAFFSINTERFACE_H__
+
+
+int yaffs_Initialise(unsigned nBlocks);
+
+#endif
+
diff -x asm -Naur linux-2.4.30-orig/fs/yaffs/yportenv.h linux-2.4.30/fs/yaffs/yportenv.h
--- linux-2.4.30-orig/fs/yaffs/yportenv.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/fs/yaffs/yportenv.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,137 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yportenv.h: Portable services used by yaffs. This is done to allow
+ * simple migration from kernel space into app space for testing.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * $Id: yportenv.h,v 1.1 2005/10/13 15:18:48 lel Exp $
+ *
+ */
+ 
+#ifndef __YPORTENV_H__
+#define __YPORTENV_H__
+
+
+#if defined CONFIG_YAFFS_WINCE
+
+#include "ywinceenv.h"
+
+#elif  defined __KERNEL__
+
+
+
+// Linux kernel
+#include "linux/kernel.h"
+#include "linux/version.h"
+#include "linux/mm.h"
+#include "linux/string.h"
+#include "linux/slab.h"
+
+
+#define YAFFS_LOSTNFOUND_NAME		"lost+found"
+#define YAFFS_LOSTNFOUND_PREFIX		"obj"
+
+//#define YPRINTF(x) printk x
+#define YMALLOC(x) kmalloc(x,GFP_KERNEL)
+#define YFREE(x)   kfree(x)
+
+#define YAFFS_ROOT_MODE				0666
+#define YAFFS_LOSTNFOUND_MODE		0666
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec
+#define Y_TIME_CONVERT(x) (x).tv_sec
+#else
+#define Y_CURRENT_TIME CURRENT_TIME
+#define Y_TIME_CONVERT(x) (x)
+#endif
+
+#define yaffs_SumCompare(x,y) ((x) == (y))
+#define yaffs_strcmp(a,b) strcmp(a,b)
+
+#define TENDSTR "\n"
+#define TSTR(x) KERN_DEBUG x
+#define TOUT(p) printk p
+
+
+#elif defined CONFIG_YAFFS_DIRECT
+
+// Direct interface
+#include "ydirectenv.h"
+
+#elif defined CONFIG_YAFFS_UTIL
+
+// Stuff for YAFFS utilities
+
+#include "stdlib.h"
+#include "stdio.h"
+#include "string.h"
+
+#include "devextras.h"
+
+#define YMALLOC(x) malloc(x)
+#define YFREE(x)   free(x)
+
+
+//#define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s))
+//#define YALERT(s) YINFO(s)
+
+
+#define TENDSTR "\n"
+#define TSTR(x) x
+#define TOUT(p) printf p
+
+
+#define YAFFS_LOSTNFOUND_NAME		"lost+found"
+#define YAFFS_LOSTNFOUND_PREFIX		"obj"
+//#define YPRINTF(x) printf x
+
+
+#define CURRENT_TIME 0
+#define YAFFS_ROOT_MODE				0666
+#define YAFFS_LOSTNFOUND_MODE		0666
+
+#define yaffs_SumCompare(x,y) ((x) == (y))
+#define yaffs_strcmp(a,b) strcmp(a,b)
+
+#else
+// Should have specified a configuration type
+#error Unknown configuration
+
+#endif 
+
+
+extern unsigned yaffs_traceMask;
+
+#define YAFFS_TRACE_ERROR		0x0001
+#define YAFFS_TRACE_OS			0x0002
+#define YAFFS_TRACE_ALLOCATE	0x0004
+#define YAFFS_TRACE_SCAN		0x0008
+#define YAFFS_TRACE_BAD_BLOCKS	0x0010
+#define YAFFS_TRACE_ERASE		0x0020
+#define YAFFS_TRACE_GC			0x0040
+#define YAFFS_TRACE_TRACING		0x0100
+#define YAFFS_TRACE_ALWAYS		0x0200
+#define YAFFS_TRACE_BUG			0x8000
+
+#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ERROR)) TOUT(p);} while(0) 
+
+
+#ifndef CONFIG_YAFFS_WINCE
+#define YBUG() T(YAFFS_TRACE_BUG,(TSTR("==>> yaffs bug: " __FILE__ " %d" TENDSTR),__LINE__))
+#endif
+
+#endif
+
+
diff -x asm -Naur linux-2.4.30-orig/include/asm-mips/au1000_pcmcia.h linux-2.4.30/include/asm-mips/au1000_pcmcia.h
--- linux-2.4.30-orig/include/asm-mips/au1000_pcmcia.h	2005-05-12 17:57:44.000000000 +0200
+++ linux-2.4.30/include/asm-mips/au1000_pcmcia.h	2006-12-11 18:18:37.000000000 +0100
@@ -33,9 +33,9 @@
 #define AU1000_PCMCIA_IO_SPEED       (255)
 #define AU1000_PCMCIA_MEM_SPEED      (300)
 
-#define AU1X_SOCK0_IO        0xF00000000
-#define AU1X_SOCK0_PHYS_ATTR 0xF40000000
-#define AU1X_SOCK0_PHYS_MEM  0xF80000000
+#define AU1X_SOCK0_IO        0xF00000000LL
+#define AU1X_SOCK0_PHYS_ATTR 0xF40000000LL
+#define AU1X_SOCK0_PHYS_MEM  0xF80000000LL
 
 /* pcmcia socket 1 needs external glue logic so the memory map
  * differs from board to board. the general rule is that
diff -x asm -Naur linux-2.4.30-orig/include/asm-mips/au1xxx_dbdma.h linux-2.4.30/include/asm-mips/au1xxx_dbdma.h
--- linux-2.4.30-orig/include/asm-mips/au1xxx_dbdma.h	2005-05-12 17:57:44.000000000 +0200
+++ linux-2.4.30/include/asm-mips/au1xxx_dbdma.h	2006-12-11 18:18:37.000000000 +0100
@@ -94,7 +94,7 @@
 #define DDMA_STAT_H	(1 << 0)	/* Channel Halted */
 
 /* "Standard" DDMA Descriptor.
- * Must be 32-byte aligned.
+ * Must be 32-byte aligned and the size must be a multiple of 32 bytes.
  */
 typedef volatile struct au1xxx_ddma_desc {
 	u32	dscr_cmd0;		/* See below */
@@ -105,12 +105,6 @@
 	u32	dscr_dest1;		/* See below */
 	u32	dscr_stat;		/* completion status */
 	u32	dscr_nxtptr;		/* Next descriptor pointer (mostly) */
-	/* First 32bytes are HW specific!!!
-	   Lets have some SW data following.. make sure its 32bytes
-	 */
-	u32	sw_status;
-	u32 	sw_context;
-	u32	sw_reserved[6];
 } au1x_ddma_desc_t;
 
 #define DSCR_CMD0_V		(1 << 31)	/* Descriptor valid */
@@ -238,6 +232,9 @@
 #define DSCR_CMD1_FL_MASK	(0x3 << 22)	/* Flag bits */
 #define DSCR_CMD1_BC_MASK	(0x3fffff)	/* Byte count */
 
+#define DSCR_CMD1_SUPTR(x)	(((x) & 0xf) << 28)	/* upper 4 bits of src addr */
+#define DSCR_CMD1_DUPTR(x)	(((x) & 0xf) << 24)	/* upper 4 bits of dest addr */
+
 /* Flag description.
 */
 #define DSCR_CMD1_FL_MEM_STRIDE0	0
@@ -305,7 +302,7 @@
 
 
 typedef struct dbdma_chan_config {
-	spinlock_t      lock;
+	spinlock_t		lock;
 
 	u32			chan_flags;
 	u32			chan_index;
@@ -313,7 +310,7 @@
 	dbdev_tab_t		*chan_dest;
 	au1x_dma_chan_t		*chan_ptr;
 	au1x_ddma_desc_t	*chan_desc_base;
-	au1x_ddma_desc_t	*get_ptr, *put_ptr, *cur_ptr;
+	au1x_ddma_desc_t	*ptr;
 	void			*chan_callparam;
 	void (*chan_callback)(int, void *, struct pt_regs *);
 } chan_tab_t;
@@ -322,7 +319,7 @@
 #define DEV_FLAGS_ANYUSE	(1 << 1)
 #define DEV_FLAGS_OUT		(1 << 2)
 #define DEV_FLAGS_IN		(1 << 3)
-#define DEV_FLAGS_BURSTABLE (1 << 4)
+#define DEV_FLAGS_BURSTABLE	(1 << 4)
 #define DEV_FLAGS_SYNC		(1 << 5)
 /* end Ddma API definitions */
 
@@ -340,7 +337,11 @@
 
 /* Set the device width of a in/out fifo.
 */
-u32 au1xxx_dbdma_set_devwidth(u32 chanid, int bits);
+u32 _au1xxx_dbdma_set_devwidth(u32 chanid, u32 iobits, u32 membits);
+
+/* Set the parameters for src1/dest1
+ */
+u32 _au1xxx_dbdma_set_transfer_size(u32 chanid, u32 src_datums, u32 dest_datums);
 
 /* Allocate a ring of descriptors for dbdma.
 */
@@ -364,6 +365,7 @@
 void au1xxx_dbdma_dump(u32 chanid);
 
 u32 au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr );
+u32 au1xxx_dbdma_put_dscr2(u32 chanid, int nbytes);	// i3 patch
 
 u32 au1xxx_ddma_add_device( dbdev_tab_t *dev );
 
@@ -371,12 +373,14 @@
  	Some compatibilty macros --
 		Needed to make changes to API without breaking existing drivers
 */
-#define	au1xxx_dbdma_put_source(chanid,buf,nbytes)_au1xxx_dbdma_put_source(chanid, buf, nbytes, DDMA_FLAGS_IE)
+#define	au1xxx_dbdma_put_source(chanid,buf,nbytes) _au1xxx_dbdma_put_source(chanid, buf, nbytes, DDMA_FLAGS_IE)
 #define	au1xxx_dbdma_put_source_flags(chanid,buf,nbytes,flags) _au1xxx_dbdma_put_source(chanid, buf, nbytes, flags)
 
 #define au1xxx_dbdma_put_dest(chanid,buf,nbytes) _au1xxx_dbdma_put_dest(chanid, buf, nbytes, DDMA_FLAGS_IE)
 #define	au1xxx_dbdma_put_dest_flags(chanid,buf,nbytes,flags) _au1xxx_dbdma_put_dest(chanid, buf, nbytes, flags)
 
+#define au1xxx_dbdma_set_devwidth(chanid, iobits) _au1xxx_dbdma_set_devwidth(chanid, iobits, 0)
+
 /*
  *	Flags for the put_source/put_dest functions.
  */
diff -x asm -Naur linux-2.4.30-orig/include/asm-mips/mood300.h linux-2.4.30/include/asm-mips/mood300.h
--- linux-2.4.30-orig/include/asm-mips/mood300.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/include/asm-mips/mood300.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,208 @@
+/*
+ * AMD Alchemy DB1x00 Reference Boards (BUT NOT DB1200)
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	ppopov@mvista.com or source@mvista.com
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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.
+ *
+ * ########################################################################
+ *
+ * 
+ */
+#ifndef __ASM_MOOD300_H
+#define __ASM_MOOD300_H
+
+//#ifdef CONFIG_MIPS_DB1550
+#define BCSR_KSEG1_ADDR 0xAF000000
+#define NAND_PHYS_ADDR   0x20000000
+
+#define DBDMA_AC97_TX_CHAN DSCR_CMD0_PSC1_TX
+#define DBDMA_AC97_RX_CHAN DSCR_CMD0_PSC1_RX
+#define DBDMA_I2S_TX_CHAN DSCR_CMD0_PSC3_TX
+#define DBDMA_I2S_RX_CHAN DSCR_CMD0_PSC3_RX
+
+#define SPI_PSC_BASE        PSC0_BASE_ADDR
+#define AC97_PSC_BASE       PSC1_BASE_ADDR
+#define SMBUS_PSC_BASE      PSC2_BASE_ADDR
+#define I2S_PSC_BASE        PSC3_BASE_ADDR
+
+/* NAND SETTINGS */
+#define NAND_CS 2
+#define NAND_STCFG  0x00400005
+#define NAND_STTIME 0x00007774
+#define NAND_STADDR 0x12003fff
+
+/* for drivers/pcmcia/au1000_db1x00.c */
+#define BOARD_PC0_INT AU1000_GPIO_3
+#define BOARD_PC1_INT AU1000_GPIO_5
+#define BOARD_CARD_INSERTED(SOCKET) !(bcsr->status & (1<<(4+SOCKET)))
+
+/* SPI */
+#define AU1550SPI_SET_PEER	_IOW(AU1550SPI_IOC_MAGIC, 2, int)
+#define AU1550SPI_LOOPBACK	_IOW(AU1550SPI_IOC_MAGIC, 3, int)
+
+#define AU1550SPI_PEER_NET 2
+#define AU1550SPI_PEER_AVR 4
+
+#define AU1550SPI_PEER_ALL (AU1550SPI_PEER_NET | AU1550SPI_PEER_AVR)
+
+//#else
+//#define BCSR_KSEG1_ADDR 0xAE000000
+/* for drivers/pcmcia/au1000_db1x00.c */
+//#define BOARD_PC0_INT AU1000_GPIO_2
+//#define BOARD_PC1_INT AU1000_GPIO_5
+//#define BOARD_CARD_INSERTED(SOCKET) !(bcsr->status & (1<<(4+SOCKET)))
+//#endif
+
+/*
+ * Overlay data structure of the Db1x00 board registers.
+ * Registers located at physical 0E0000xx, KSEG1 0xAE0000xx
+ */
+typedef volatile struct
+{
+	/*00*/	unsigned short whoami;
+			unsigned short reserved0;
+	/*04*/	unsigned short status;
+			unsigned short reserved1;
+	/*08*/	unsigned short switches;
+			unsigned short reserved2;
+	/*0C*/	unsigned short resets;
+			unsigned short reserved3;
+	/*10*/	unsigned short pcmcia;
+			unsigned short reserved4;
+	/*14*/	unsigned short specific;
+			unsigned short reserved5;
+	/*18*/	unsigned short leds;
+			unsigned short reserved6;
+	/*1C*/	unsigned short swreset;
+			unsigned short reserved7;
+
+} BCSR;
+
+static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+
+/*
+ * Register/mask bit definitions for the BCSRs
+ */
+#define BCSR_WHOAMI_DCID		0x000F
+#define BCSR_WHOAMI_CPLD		0x00F0
+#define BCSR_WHOAMI_BOARD		0x0F00
+
+#define BCSR_STATUS_PC0VS		0x0003
+#define BCSR_STATUS_PC1VS		0x000C
+#define BCSR_STATUS_PC0FI		0x0010
+#define BCSR_STATUS_PC1FI		0x0020
+#define BCSR_STATUS_FLASHBUSY		0x0100
+#define BCSR_STATUS_ROMBUSY		0x0400
+#define BCSR_STATUS_SWAPBOOT		0x2000
+#define BCSR_STATUS_FLASHDEN		0xC000
+
+#define BCSR_SWITCHES_DIP		0x00FF
+#define BCSR_SWITCHES_DIP_1		0x0080
+#define BCSR_SWITCHES_DIP_2		0x0040
+#define BCSR_SWITCHES_DIP_3		0x0020
+#define BCSR_SWITCHES_DIP_4		0x0010
+#define BCSR_SWITCHES_DIP_5		0x0008
+#define BCSR_SWITCHES_DIP_6		0x0004
+#define BCSR_SWITCHES_DIP_7		0x0002
+#define BCSR_SWITCHES_DIP_8		0x0001
+#define BCSR_SWITCHES_ROTARY		0x0F00
+
+#define BCSR_RESETS_PHY0		0x0001
+#define BCSR_RESETS_PHY1		0x0002
+#define BCSR_RESETS_DC			0x0004
+#define BCSR_RESETS_FIR_SEL		0x2000
+#define BCSR_RESETS_IRDA_MODE_MASK	0xC000
+#define BCSR_RESETS_IRDA_MODE_FULL	0x0000
+#define BCSR_RESETS_IRDA_MODE_OFF	0x4000
+#define BCSR_RESETS_IRDA_MODE_2_3	0x8000
+#define BCSR_RESETS_IRDA_MODE_1_3	0xC000
+
+#define BCSR_PCMCIA_PC0VPP		0x0003
+#define BCSR_PCMCIA_PC0VCC		0x000C
+#define BCSR_PCMCIA_PC0DRVEN		0x0010
+#define BCSR_PCMCIA_PC0RST		0x0080
+#define BCSR_PCMCIA_PC1VPP		0x0300
+#define BCSR_PCMCIA_PC1VCC		0x0C00
+#define BCSR_PCMCIA_PC1DRVEN		0x1000
+#define BCSR_PCMCIA_PC1RST		0x8000
+
+#define BCSR_BOARD_PCIM66EN		0x0001
+#define BCSR_BOARD_SD0_PWR		0x0040
+#define BCSR_BOARD_SD1_PWR		0x0080
+#define BCSR_BOARD_PCIM33		0x0100
+#define BCSR_BOARD_GPIO200RST		0x0400
+#define BCSR_BOARD_PCICFG		0x1000
+#define BCSR_BOARD_SD0_WP		0x4000
+#define BCSR_BOARD_SD1_WP		0x8000
+
+#define BCSR_LEDS_DECIMALS		0x0003
+#define BCSR_LEDS_LED0			0x0100
+#define BCSR_LEDS_LED1			0x0200
+#define BCSR_LEDS_LED2			0x0400
+#define BCSR_LEDS_LED3			0x0800
+
+#define BCSR_SWRESET_RESET		0x0080
+
+/* MTD CONFIG OPTIONS */
+#if defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER)
+#define DB1X00_BOTH_BANKS
+#elif defined(CONFIG_MTD_DB1X00_BOOT) && !defined(CONFIG_MTD_DB1X00_USER)
+#define DB1X00_BOOT_ONLY
+#elif !defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER)
+#define DB1X00_USER_ONLY
+#endif
+
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX) && (defined(CONFIG_MIPS_DB1550) || defined(CONFIG_MIPS_I3_MMB3))
+/*
+ * Daughter card information.
+ */
+#define DAUGHTER_CARD_IRQ		(AU1000_GPIO_8)
+/* DC_IDE */
+#define AU1XXX_ATA_PHYS_ADDR		(0x0C000000)
+#define AU1XXX_ATA_REG_OFFSET		(5)	
+#define AU1XXX_ATA_PHYS_LEN             (0x1000)
+#define AU1XXX_ATA_INT                  AU1500_GPIO_207         //for i3m, otherwise 5: PCMCIA
+#define AU1XXX_ATA_DDMA_REQ             DSCR_CMD0_DMA_REQ2      //for i3m. GPIO_208
+
+#endif /* CONFIG_MIPS_DB1550 */
+
+/* Timing values as described in databook, * ns value stripped of 
+ * lower 2 bits.
+ * These defines are here rather than an SOC1550 generic file because 
+ * the parts chosen on another board may be different and may require 
+ * different timings.
+ */
+#define NAND_T_H                        (18 >> 2)
+#define NAND_T_PUL                      (30 >> 2)
+#define NAND_T_SU                       (30 >> 2)
+#define NAND_T_WH                       (30 >> 2)
+
+/* Bitfield shift amounts */
+#define NAND_T_H_SHIFT          0
+#define NAND_T_PUL_SHIFT        4
+#define NAND_T_SU_SHIFT         8
+#define NAND_T_WH_SHIFT         12
+
+#define NAND_TIMING     ((NAND_T_H   & 0xF)     << NAND_T_H_SHIFT)   | \
+                        ((NAND_T_PUL & 0xF)     << NAND_T_PUL_SHIFT) | \
+                        ((NAND_T_SU  & 0xF)     << NAND_T_SU_SHIFT)  | \
+                        ((NAND_T_WH  & 0xF)     << NAND_T_WH_SHIFT)
+
+#endif /* __ASM_MOOD300_H */
+
diff -x asm -Naur linux-2.4.30-orig/include/asm-mips/pgtable-64.h linux-2.4.30/include/asm-mips/pgtable-64.h
--- linux-2.4.30-orig/include/asm-mips/pgtable-64.h	2005-05-12 17:57:45.000000000 +0200
+++ linux-2.4.30/include/asm-mips/pgtable-64.h	2006-12-11 18:18:37.000000000 +0100
@@ -142,22 +142,44 @@
  * not possible, use pte_get_and_clear to obtain the old pte
  * value and then use set_pte to update it.  -ben
  */
-static inline void set_pte(pte_t *ptep, pte_t pte)
-{
-	ptep->pte_high = pte.pte_high;
-	smp_wmb();
-	ptep->pte_low = pte.pte_low;
-
-	if (pte_val(pte) & _PAGE_GLOBAL) {
-		pte_t *buddy = ptep_buddy(ptep);
-		/*
-		 * Make sure the buddy is global too (if it's !none,
-		 * it better already be global)
-		 */
-		if (pte_none(*buddy))
-			buddy->pte_low |= _PAGE_GLOBAL;
-	}
-}
+static inline void set_pte(pte_t *ptep, pte_t pte) 
+{ 
+   u32 high;
+   u32 low;
+// 
+// A physical address in the range 0x0_40000000 through 0x0_4fffffff 
+// gets mapped to the 36-bit range 0x4_40000000 through 0x4_4fffffff. 
+// See linux/arch/mips/au1000/common/setup.c au1500_fixup_bigphys_addr() 
+// 
+   if( ((pte.pte_high >> 22) & 0xff) == 0x04 )
+   { 
+
+      /* 0x0_4xxxxxxx => 0x4_4xxxxxxx  
+       * (0x7 << 3) Uncached, mergeable, gatherable for au1550, uncached for
+       *	    others.
+       */
+      high = pte.pte_high | (0x4 << 26) | (0x7 << 3);
+      low  = pte.pte_low;
+   }
+   else {
+      high = pte.pte_high;
+      low  = pte.pte_low;
+   }
+
+   ptep->pte_high = high;
+   smp_wmb();
+   ptep->pte_low = low;
+   
+   if (pte_val(pte) & _PAGE_GLOBAL) {
+      pte_t *buddy = ptep_buddy(ptep);
+      /*
+       * Make sure the buddy is global too (if it's !none,
+       * it better already be global)
+       */
+      if (pte_none(*buddy))
+	 buddy->pte_low |= _PAGE_GLOBAL;
+   }
+} 
 
 static inline pte_t 
 __mk_pte(unsigned long page_nr, pgprot_t pgprot)
diff -x asm -Naur linux-2.4.30-orig/include/linux/i2c-algo-au1550.h linux-2.4.30/include/linux/i2c-algo-au1550.h
--- linux-2.4.30-orig/include/linux/i2c-algo-au1550.h	2005-05-12 17:57:53.000000000 +0200
+++ linux-2.4.30/include/linux/i2c-algo-au1550.h	2006-12-11 18:18:37.000000000 +0100
@@ -21,10 +21,13 @@
 
 struct i2c_algo_au1550_data {
 	u32	psc_base;
+        u32     psc_irq;
 	int	xfer_timeout;
 	int	ack_timeout;
 };
 
+#define DRIVER_NAME "au1550-i2c"
+
 int i2c_au1550_add_bus(struct i2c_adapter *);
 int i2c_au1550_del_bus(struct i2c_adapter *);
 
diff -x asm -Naur linux-2.4.30-orig/include/linux/i2c-id.h linux-2.4.30/include/linux/i2c-id.h
--- linux-2.4.30-orig/include/linux/i2c-id.h	2005-05-12 17:57:53.000000000 +0200
+++ linux-2.4.30/include/linux/i2c-id.h	2006-12-11 18:18:37.000000000 +0100
@@ -20,7 +20,7 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.		     */
 /* ------------------------------------------------------------------------- */
 
-/* $Id: i2c-id.h,v 1.35 2001/08/12 17:22:20 mds Exp $ */
+/* $Id: i2c-id.h,v 1.1.1.1 2005/09/14 15:11:15 lel Exp $ */
 
 #ifndef I2C_ID_H
 #define I2C_ID_H
diff -x asm -Naur linux-2.4.30-orig/include/linux/i2c.h linux-2.4.30/include/linux/i2c.h
--- linux-2.4.30-orig/include/linux/i2c.h	2005-05-12 17:57:53.000000000 +0200
+++ linux-2.4.30/include/linux/i2c.h	2006-12-11 18:18:37.000000000 +0100
@@ -23,7 +23,7 @@
 /* With some changes from Ky�sti M�lkki <kmalkki@cc.hut.fi> and
    Frodo Looijaard <frodol@dds.nl> */
 
-/* $Id: i2c.h,v 1.46 2001/08/31 00:04:07 phil Exp $ */
+/* $Id: i2c.h,v 1.2 2005/09/28 14:19:48 lel Exp $ */
 
 #ifndef I2C_H
 #define I2C_H
@@ -47,9 +47,11 @@
 
 /* --- General options ------------------------------------------------	*/
 
-#define I2C_ADAP_MAX	16		/* control memory consumption	*/
+#define I2C_ALGO_MAX	4		/* control memory consumption	*/
+#define I2C_ADAP_MAX	16
 #define I2C_DRIVER_MAX	16
 #define I2C_CLIENT_MAX	32
+#define I2C_DUMMY_MAX 4
 
 struct i2c_algorithm;
 struct i2c_adapter;
@@ -76,9 +78,8 @@
  * Some adapter types (i.e. PCF 8584 based ones) may support slave behaviuor. 
  * This is not tested/implemented yet and will change in the future.
  */
-extern int i2c_slave_send(struct i2c_client *,char*,int);
-extern int i2c_slave_recv(struct i2c_client *,char*,int);
-
+extern int i2c_slave_send(struct i2c_adapter *,char*,int);
+extern int i2c_slave_recv(struct i2c_adapter *,char*,int);
 
 
 /* This is the very generalized SMBus access routine. You probably do not
@@ -214,6 +215,15 @@
 	u32 (*functionality) (struct i2c_adapter *);
 };
 
+
+struct i2c_buffer {
+    unsigned char *data;
+    unsigned int buffsize;
+    unsigned int writepos;
+    unsigned int readpos;
+};
+
+
 /*
  * i2c_adapter is the structure used to identify a physical i2c bus along
  * with the access algorithms necessary to access it.
@@ -222,6 +232,7 @@
 	char name[32];	/* some useful name to identify the adapter	*/
 	unsigned int id;/* == is algo->id | hwdep.struct->id, 		*/
 			/* for registered values see below		*/
+        unsigned int mode;              /* Slave or master? */
 	struct i2c_algorithm *algo;/* the algorithm to access the bus	*/
 	void *algo_data;
 
@@ -252,6 +263,13 @@
 	/* No need to set this when you initialize the adapter          */
 	int inode;
 #endif /* def CONFIG_PROC_FS */
+
+    /*** BEGIN I3 Mods ***/
+    wait_queue_head_t readq;
+    struct fasync_struct *fasync;
+    struct i2c_buffer readbuf;
+    spinlock_t intlock;
+    /*** END I3 Mods ***/
 };
 
 /*flags for the driver struct: */
@@ -356,6 +374,8 @@
 /* Return 1 if adapter supports everything we need, 0 if not. */
 extern int i2c_check_functionality (struct i2c_adapter *adap, u32 func);
 
+__inline__ int i2c_buffer_write(struct i2c_buffer *buffer, unsigned char *inByte);
+__inline__ int i2c_buffer_read(struct i2c_buffer *buffer, unsigned char *outByte);
 #endif /* __KERNEL__ */
 
 /*
@@ -469,6 +489,13 @@
 				/* written byte (except address)	*/
 #define I2C_MDELAY	0x0706	/* millisec delay between written bytes */
 
+/* ... i2c-algo-au1550 ... */
+#define I2C_SET_SLAVE_MODE   0x0730  /* Set device to slave mode (with specified address) */
+#define I2C_SET_MASTER_MODE  0x0731  /* Set device to master mode */
+
+#define I2C_MASTER_MODE  0  /* Device is in master mode */
+#define I2C_SLAVE_MODE   1  /* Device is in slave mode */
+
 /* ----- I2C-DEV: char device interface stuff ------------------------- */
 
 #define I2C_MAJOR	89		/* Device major number		*/
diff -x asm -Naur linux-2.4.30-orig/include/linux/input.h linux-2.4.30/include/linux/input.h
--- linux-2.4.30-orig/include/linux/input.h	2005-05-12 17:57:53.000000000 +0200
+++ linux-2.4.30/include/linux/input.h	2006-12-11 18:18:37.000000000 +0100
@@ -2,7 +2,7 @@
 #define _INPUT_H
 
 /*
- * $Id: input.h,v 1.34 2001/05/28 09:06:44 vojtech Exp $
+ * $Id: input.h,v 1.2 2005/09/28 14:19:48 lel Exp $
  *
  *  Copyright (c) 1999-2000 Vojtech Pavlik
  *
@@ -427,6 +427,7 @@
 
 #define MSC_SERIAL		0x00
 #define MSC_PULSELED		0x01
+#define MSC_SCAN		0x04	/* i3, same value as in 2.6 */
 #define MSC_MAX			0x07
 
 /*
diff -x asm -Naur linux-2.4.30-orig/include/linux/jffs2.h linux-2.4.30/include/linux/jffs2.h
--- linux-2.4.30-orig/include/linux/jffs2.h	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/include/linux/jffs2.h	2006-12-11 18:18:37.000000000 +0100
@@ -3,12 +3,12 @@
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@redhat.com>
+ * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in the 
  * jffs2 directory.
  *
- * $Id: jffs2.h,v 1.32 2004/05/05 11:57:53 dwmw2 Exp $
+ * $Id: jffs2.h,v 1.2 2005/09/28 14:19:48 lel Exp $
  *
  */
 
@@ -43,6 +43,8 @@
 #define JFFS2_COMPR_COPY	0x04
 #define JFFS2_COMPR_DYNRUBIN	0x05
 #define JFFS2_COMPR_ZLIB	0x06
+#define JFFS2_COMPR_LZO         0x07
+#define JFFS2_COMPR_LZARI       0x08
 /* Compatibility flags. */
 #define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
 #define JFFS2_NODE_ACCURATE 0x2000
diff -x asm -Naur linux-2.4.30-orig/include/linux/jffs2_fs_i.h linux-2.4.30/include/linux/jffs2_fs_i.h
--- linux-2.4.30-orig/include/linux/jffs2_fs_i.h	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/include/linux/jffs2_fs_i.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,10 +1,11 @@
-/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */
+/* $Id: jffs2_fs_i.h,v 1.2 2005/09/28 14:19:48 lel Exp $ */
 
 #ifndef _JFFS2_FS_I
 #define _JFFS2_FS_I
 
 #include <linux/version.h>
 #include <linux/rbtree.h>
+#include <asm/semaphore.h>
 
 struct jffs2_inode_info {
 	/* We need an internal semaphore similar to inode->i_sem.
diff -x asm -Naur linux-2.4.30-orig/include/linux/jffs2_fs_sb.h linux-2.4.30/include/linux/jffs2_fs_sb.h
--- linux-2.4.30-orig/include/linux/jffs2_fs_sb.h	2005-05-12 18:01:30.000000000 +0200
+++ linux-2.4.30/include/linux/jffs2_fs_sb.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,4 +1,4 @@
-/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */
+/* $Id: jffs2_fs_sb.h,v 1.2 2005/09/28 14:19:48 lel Exp $ */
 
 #ifndef _JFFS2_FS_SB
 #define _JFFS2_FS_SB
@@ -11,9 +11,11 @@
 #include <linux/timer.h>
 #include <linux/wait.h>
 #include <linux/list.h>
+#include <linux/rwsem.h>
 
 #define JFFS2_SB_FLAG_RO 1
-#define JFFS2_SB_FLAG_MOUNTING 2
+#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
+#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
 
 struct jffs2_inodirty;
 
@@ -35,9 +37,7 @@
 
 	struct semaphore alloc_sem;	/* Used to protect all the following 
 					   fields, and also to protect against
-					   out-of-order writing of nodes.
-					   And GC.
-					*/
+					   out-of-order writing of nodes. And GC. */
 	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
 					 (i.e. zero for OOB CLEANMARKER */
 
@@ -95,7 +95,7 @@
 	   to an obsoleted node. I don't like this. Alternatives welcomed. */
 	struct semaphore erase_free_sem;
 
-#ifdef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
 	/* Write-behind buffer for NAND flash */
 	unsigned char *wbuf;
 	uint32_t wbuf_ofs;
@@ -103,6 +103,8 @@
 	uint32_t wbuf_pagesize;
 	struct jffs2_inodirty *wbuf_inodes;
 
+	struct rw_semaphore wbuf_sem;	/* Protects the write buffer */
+
 	/* Information about out-of-band area usage... */
 	struct nand_oobinfo *oobinfo;
 	uint32_t badblock_pos;
diff -x asm -Naur linux-2.4.30-orig/include/linux/lsdbg.h linux-2.4.30/include/linux/lsdbg.h
--- linux-2.4.30-orig/include/linux/lsdbg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/include/linux/lsdbg.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,26 @@
+//***************************************************************************
+// FILENAME: lsdbg.h
+//***************************************************************************
+//   Copyright (c) 2005 i3 micro technology ab (publ)
+//
+//   May be copied or modified under the terms
+//   of the GNU General Public License
+//
+// ***************************************************************************
+// AUTHOR: Lars Salomonsson
+//
+// DESCRIPTION:
+//   This file contains defines for the lsdbg module.
+//
+//***************************************************************************
+
+#ifndef _LSDBG_H_
+#define _LSDBG_H_
+
+#if defined(CONFIG_LSDBG)
+void lsdbg_trace(char *msg, int par);
+#else
+#define lsdbg_trace(x,y)
+#endif
+
+#endif // _LSDBG_H_
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/bbm.h linux-2.4.30/include/linux/mtd/bbm.h
--- linux-2.4.30-orig/include/linux/mtd/bbm.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/include/linux/mtd/bbm.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,122 @@
+/*
+ *  linux/include/linux/mtd/bbm.h
+ *
+ *  NAND family Bad Block Management (BBM) header file
+ *    - Bad Block Table (BBT) implementation
+ *
+ *  Copyright (c) 2005 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ *  Copyright (c) 2000-2005
+ *  Thomas Gleixner <tglx@linuxtronix.de>
+ *
+ */
+#ifndef __LINUX_MTD_BBM_H
+#define __LINUX_MTD_BBM_H
+
+/* The maximum number of NAND chips in an array */
+#define NAND_MAX_CHIPS		8
+
+/**
+ * struct nand_bbt_descr - bad block table descriptor
+ * @param options	options for this descriptor
+ * @param pages		the page(s) where we find the bbt, used with
+ * 			option BBT_ABSPAGE when bbt is searched,
+ * 			then we store the found bbts pages here.
+ *			Its an array and supports up to 8 chips now
+ * @param offs		offset of the pattern in the oob area of the page
+ * @param veroffs	offset of the bbt version counter in the oob are of the page
+ * @param version	version read from the bbt page during scan
+ * @param len		length of the pattern, if 0 no pattern check is performed
+ * @param maxblocks	maximum number of blocks to search for a bbt. This number of
+ *			blocks is reserved at the end of the device 
+ *			where the tables are written.
+ * @param reserved_block_code	if non-0, this pattern denotes a reserved
+ *			(rather than bad) block in the stored bbt
+ * @param pattern	pattern to identify bad block table or factory marked
+ *			good / bad blocks, can be NULL, if len = 0
+ *
+ * Descriptor for the bad block table marker and the descriptor for the
+ * pattern which identifies good and bad blocks. The assumption is made
+ * that the pattern and the version count are always located in the oob area
+ * of the first block.
+ */
+struct nand_bbt_descr {
+	int options;
+	int pages[NAND_MAX_CHIPS];
+	int offs;
+	int veroffs;
+	uint8_t version[NAND_MAX_CHIPS];
+	int len;
+	int maxblocks;
+	int reserved_block_code;
+	uint8_t *pattern;
+};
+
+/* Options for the bad block table descriptors */
+
+/* The number of bits used per block in the bbt on the device */
+#define NAND_BBT_NRBITS_MSK	0x0000000F
+#define NAND_BBT_1BIT		0x00000001
+#define NAND_BBT_2BIT		0x00000002
+#define NAND_BBT_4BIT		0x00000004
+#define NAND_BBT_8BIT		0x00000008
+/* The bad block table is in the last good block of the device */
+#define NAND_BBT_LASTBLOCK	0x00000010
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_ABSPAGE	0x00000020
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_SEARCH		0x00000040
+/* bbt is stored per chip on multichip devices */
+#define NAND_BBT_PERCHIP	0x00000080
+/* bbt has a version counter at offset veroffs */
+#define NAND_BBT_VERSION	0x00000100
+/* Create a bbt if none axists */
+#define NAND_BBT_CREATE		0x00000200
+/* Search good / bad pattern through all pages of a block */
+#define NAND_BBT_SCANALLPAGES	0x00000400
+/* Scan block empty during good / bad block scan */
+#define NAND_BBT_SCANEMPTY	0x00000800
+/* Write bbt if neccecary */
+#define NAND_BBT_WRITE		0x00001000
+/* Read and write back block contents when writing bbt */
+#define NAND_BBT_SAVECONTENT	0x00002000
+/* Search good / bad pattern on the first and the second page */
+#define NAND_BBT_SCAN2NDPAGE	0x00004000
+
+/* The maximum number of blocks to scan for a bbt */
+#define NAND_BBT_SCAN_MAXBLOCKS	4
+
+/*
+ * Constants for oob configuration
+ */
+#define ONENAND_BADBLOCK_POS	0
+
+/**
+ * struct bbt_info - [GENERIC] Bad Block Table data structure
+ * @param bbt_erase_shift	[INTERN] number of address bits in a bbt entry
+ * @param badblockpos		[INTERN] position of the bad block marker in the oob area
+ * @param bbt			[INTERN] bad block table pointer
+ * @param badblock_pattern	[REPLACEABLE] bad block scan pattern used for initial bad block scan
+ * @param priv			[OPTIONAL] pointer to private bbm date
+ */
+struct bbm_info {
+	int bbt_erase_shift;
+	int badblockpos;
+	int options;
+
+	uint8_t *bbt;
+
+	int (*isbad_bbt)(struct mtd_info *mtd, loff_t ofs, int allowbbt);
+
+	/* TODO Add more NAND specific fileds */
+	struct nand_bbt_descr *badblock_pattern;
+
+	void *priv;
+};
+
+/* OneNAND BBT interface */
+extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
+extern int onenand_default_bbt(struct mtd_info *mtd);
+
+#endif	/* __LINUX_MTD_BBM_H */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/blktrans.h linux-2.4.30/include/linux/mtd/blktrans.h
--- linux-2.4.30-orig/include/linux/mtd/blktrans.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/blktrans.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: blktrans.h,v 1.5 2003/06/23 12:00:08 dwmw2 Exp $
+ * $Id: blktrans.h,v 1.3 2005/10/12 14:52:45 lel Exp $
  *
  * (C) 2003 David Woodhouse <dwmw2@infradead.org>
  *
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/cfi.h linux-2.4.30/include/linux/mtd/cfi.h
--- linux-2.4.30-orig/include/linux/mtd/cfi.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/cfi.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,7 +1,7 @@
 
 /* Common Flash Interface structures 
  * See http://support.intel.com/design/flash/technote/index.htm
- * $Id: cfi.h,v 1.41 2004/04/15 12:16:12 gleixner Exp $
+ * $Id: cfi.h,v 1.3 2005/10/12 14:52:45 lel Exp $
  */
 
 #ifndef __MTD_CFI_H__
@@ -13,200 +13,74 @@
 #include <linux/types.h>
 #include <linux/interrupt.h>
 #include <linux/mtd/flashchip.h>
+#include <linux/mtd/map.h>
 #include <linux/mtd/cfi_endian.h>
 
-/*
- * You can optimize the code size and performance by defining only 
- * the geometry(ies) available on your hardware.
- * CFIDEV_INTERLEAVE_n, where  represents the interleave (number of chips to fill the bus width)
- * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes)
- *
- * By default, all (known) geometries are supported.
- */
-
-#ifndef CONFIG_MTD_CFI_GEOMETRY
-
-/* The default case - support all but 64-bit, which has
-   a performance penalty */
-
-#define CFIDEV_INTERLEAVE_1 (1)
-#define CFIDEV_INTERLEAVE_2 (2)
-#define CFIDEV_INTERLEAVE_4 (4)
-
-#define CFIDEV_BUSWIDTH_1 (1)
-#define CFIDEV_BUSWIDTH_2 (2)
-#define CFIDEV_BUSWIDTH_4 (4)
-
-typedef __u32 cfi_word;
-
-#else
-
-/* Explicitly configured buswidth/interleave support */
-
 #ifdef CONFIG_MTD_CFI_I1
-#define CFIDEV_INTERLEAVE_1 (1)
-#endif
-#ifdef CONFIG_MTD_CFI_I2
-#define CFIDEV_INTERLEAVE_2 (2)
-#endif
-#ifdef CONFIG_MTD_CFI_I4
-#define CFIDEV_INTERLEAVE_4 (4)
-#endif
-#ifdef CONFIG_MTD_CFI_I8
-#define CFIDEV_INTERLEAVE_8 (8)
-#endif
-
-#ifdef CONFIG_MTD_CFI_B1
-#define CFIDEV_BUSWIDTH_1 (1)
-#endif
-#ifdef CONFIG_MTD_CFI_B2
-#define CFIDEV_BUSWIDTH_2 (2)
-#endif
-#ifdef CONFIG_MTD_CFI_B4
-#define CFIDEV_BUSWIDTH_4 (4)
-#endif
-#ifdef CONFIG_MTD_CFI_B8
-#define CFIDEV_BUSWIDTH_8 (8)
-#endif
-
-/* pick the largest necessary */
-#ifdef CONFIG_MTD_CFI_B8
-typedef __u64 cfi_word;
-
-/* This only works if asm/io.h is included first */
-#ifndef __raw_readq
-#define __raw_readq(addr)	(*(volatile __u64 *)(addr))
-#endif
-#ifndef __raw_writeq
-#define __raw_writeq(v, addr)	(*(volatile __u64 *)(addr) = (v))
-#endif
-#define CFI_WORD_64
-#else  /* CONFIG_MTD_CFI_B8 */
-/* All others can use 32-bits. It's probably more efficient than
-   the smaller types anyway */
-typedef __u32 cfi_word;
-#endif /* CONFIG_MTD_CFI_B8 */
-
-#endif
-
-/*
- * The following macros are used to select the code to execute:
- *   cfi_buswidth_is_*()
- *   cfi_interleave_is_*()
- *   [where * is either 1, 2, 4, or 8]
- * Those macros should be used with 'if' statements.  If only one of few
- * geometry arrangements are selected, they expand to constants thus allowing
- * the compiler (most of them being 0) to optimize away all the unneeded code,
- * while still validating the syntax (which is not possible with embedded 
- * #if ... #endif constructs).
- * The exception to this is the 64-bit versions, which need an extension
- * to the cfi_word type, and cause compiler warnings about shifts being
- * out of range.
- */
-
-#ifdef CFIDEV_INTERLEAVE_1
-# ifdef CFIDEV_INTERLEAVE
-#  undef CFIDEV_INTERLEAVE
-#  define CFIDEV_INTERLEAVE (cfi->interleave)
-# else
-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
-# endif
-# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
+#define cfi_interleave(cfi) 1
+#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1)
 #else
-# define cfi_interleave_is_1() (0)
+#define cfi_interleave_is_1(cfi) (0)
 #endif
 
-#ifdef CFIDEV_INTERLEAVE_2
-# ifdef CFIDEV_INTERLEAVE
-#  undef CFIDEV_INTERLEAVE
-#  define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I2
+# ifdef cfi_interleave
+#  undef cfi_interleave
+#  define cfi_interleave(cfi) ((cfi)->interleave)
 # else
-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
+#  define cfi_interleave(cfi) 2
 # endif
-# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
+#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2)
 #else
-# define cfi_interleave_is_2() (0)
+#define cfi_interleave_is_2(cfi) (0)
 #endif
 
-#ifdef CFIDEV_INTERLEAVE_4
-# ifdef CFIDEV_INTERLEAVE
-#  undef CFIDEV_INTERLEAVE
-#  define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I4
+# ifdef cfi_interleave
+#  undef cfi_interleave
+#  define cfi_interleave(cfi) ((cfi)->interleave)
 # else
-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
+#  define cfi_interleave(cfi) 4
 # endif
-# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
+#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4)
 #else
-# define cfi_interleave_is_4() (0)
+#define cfi_interleave_is_4(cfi) (0)
 #endif
 
-#ifdef CFIDEV_INTERLEAVE_8
-# ifdef CFIDEV_INTERLEAVE
-#  undef CFIDEV_INTERLEAVE
-#  define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I8
+# ifdef cfi_interleave
+#  undef cfi_interleave
+#  define cfi_interleave(cfi) ((cfi)->interleave)
 # else
-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8
+#  define cfi_interleave(cfi) 8
 # endif
-# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8)
+#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8)
 #else
-# define cfi_interleave_is_8() (0)
+#define cfi_interleave_is_8(cfi) (0)
 #endif
 
-#ifndef CFIDEV_INTERLEAVE
-#error You must define at least one interleave to support!
+static inline int cfi_interleave_supported(int i)
+{
+	switch (i) {
+#ifdef CONFIG_MTD_CFI_I1
+	case 1:
 #endif
-
-#ifdef CFIDEV_BUSWIDTH_1
-# ifdef CFIDEV_BUSWIDTH
-#  undef CFIDEV_BUSWIDTH
-#  define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
-# endif
-# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
-#else
-# define cfi_buswidth_is_1() (0)
+#ifdef CONFIG_MTD_CFI_I2
+	case 2:
 #endif
-
-#ifdef CFIDEV_BUSWIDTH_2
-# ifdef CFIDEV_BUSWIDTH
-#  undef CFIDEV_BUSWIDTH
-#  define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
-# endif
-# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
-#else
-# define cfi_buswidth_is_2() (0)
+#ifdef CONFIG_MTD_CFI_I4
+	case 4:
 #endif
-
-#ifdef CFIDEV_BUSWIDTH_4
-# ifdef CFIDEV_BUSWIDTH
-#  undef CFIDEV_BUSWIDTH
-#  define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
-# endif
-# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
-#else
-# define cfi_buswidth_is_4() (0)
+#ifdef CONFIG_MTD_CFI_I8
+	case 8:
 #endif
+		return 1;
 
-#ifdef CFIDEV_BUSWIDTH_8
-# ifdef CFIDEV_BUSWIDTH
-#  undef CFIDEV_BUSWIDTH
-#  define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8
-# endif
-# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8)
-#else
-# define cfi_buswidth_is_8() (0)
-#endif
+	default:
+		return 0;
+	}
+}
 
-#ifndef CFIDEV_BUSWIDTH
-#error You must define at least one bus width to support!
-#endif
 
 /* NB: these values must represents the number of bytes needed to meet the 
  *     device type (x8, x16, x32).  Eg. a 32 bit device is 4 x 8 bytes. 
@@ -223,103 +97,141 @@
 
 /* Basic Query Structure */
 struct cfi_ident {
-  __u8  qry[3];
-  __u16 P_ID;
-  __u16 P_ADR;
-  __u16 A_ID;
-  __u16 A_ADR;
-  __u8  VccMin;
-  __u8  VccMax;
-  __u8  VppMin;
-  __u8  VppMax;
-  __u8  WordWriteTimeoutTyp;
-  __u8  BufWriteTimeoutTyp;
-  __u8  BlockEraseTimeoutTyp;
-  __u8  ChipEraseTimeoutTyp;
-  __u8  WordWriteTimeoutMax;
-  __u8  BufWriteTimeoutMax;
-  __u8  BlockEraseTimeoutMax;
-  __u8  ChipEraseTimeoutMax;
-  __u8  DevSize;
-  __u16 InterfaceDesc;
-  __u16 MaxBufWriteSize;
-  __u8  NumEraseRegions;
-  __u32 EraseRegionInfo[0]; /* Not host ordered */
+	uint8_t  qry[3];
+	uint16_t P_ID;
+	uint16_t P_ADR;
+	uint16_t A_ID;
+	uint16_t A_ADR;
+	uint8_t  VccMin;
+	uint8_t  VccMax;
+	uint8_t  VppMin;
+	uint8_t  VppMax;
+	uint8_t  WordWriteTimeoutTyp;
+	uint8_t  BufWriteTimeoutTyp;
+	uint8_t  BlockEraseTimeoutTyp;
+	uint8_t  ChipEraseTimeoutTyp;
+	uint8_t  WordWriteTimeoutMax;
+	uint8_t  BufWriteTimeoutMax;
+	uint8_t  BlockEraseTimeoutMax;
+	uint8_t  ChipEraseTimeoutMax;
+	uint8_t  DevSize;
+	uint16_t InterfaceDesc;
+	uint16_t MaxBufWriteSize;
+	uint8_t  NumEraseRegions;
+	uint32_t EraseRegionInfo[0]; /* Not host ordered */
 } __attribute__((packed));
 
 /* Extended Query Structure for both PRI and ALT */
 
 struct cfi_extquery {
-  __u8  pri[3];
-  __u8  MajorVersion;
-  __u8  MinorVersion;
+	uint8_t  pri[3];
+	uint8_t  MajorVersion;
+	uint8_t  MinorVersion;
 } __attribute__((packed));
 
 /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
 
 struct cfi_pri_intelext {
-  __u8  pri[3];
-  __u8  MajorVersion;
-  __u8  MinorVersion;
-  __u32 FeatureSupport; /* if bit 31 is set then an additional __u32 feature
-			   block follows - FIXME - not currently supported */
-  __u8  SuspendCmdSupport;
-  __u16 BlkStatusRegMask;
-  __u8  VccOptimal;
-  __u8  VppOptimal;
-  __u8  NumProtectionFields;
-  __u16 ProtRegAddr;
-  __u8  FactProtRegSize;
-  __u8  UserProtRegSize;
+	uint8_t  pri[3];
+	uint8_t  MajorVersion;
+	uint8_t  MinorVersion;
+	uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature
+				    block follows - FIXME - not currently supported */
+	uint8_t  SuspendCmdSupport;
+	uint16_t BlkStatusRegMask;
+	uint8_t  VccOptimal;
+	uint8_t  VppOptimal;
+	uint8_t  NumProtectionFields;
+	uint16_t ProtRegAddr;
+	uint8_t  FactProtRegSize;
+	uint8_t  UserProtRegSize;
+	uint8_t  extra[0];
+} __attribute__((packed));
+
+struct cfi_intelext_otpinfo {
+	uint32_t ProtRegAddr;
+	uint16_t FactGroups;
+	uint8_t  FactProtRegSize;
+	uint16_t UserGroups;
+	uint8_t  UserProtRegSize;
+} __attribute__((packed));
+
+struct cfi_intelext_blockinfo {
+	uint16_t NumIdentBlocks;
+	uint16_t BlockSize;
+	uint16_t MinBlockEraseCycles;
+	uint8_t  BitsPerCell;
+	uint8_t  BlockCap;
+} __attribute__((packed));
+
+struct cfi_intelext_regioninfo {
+	uint16_t NumIdentPartitions;
+	uint8_t  NumOpAllowed;
+	uint8_t  NumOpAllowedSimProgMode;
+	uint8_t  NumOpAllowedSimEraMode;
+	uint8_t  NumBlockTypes;
+	struct cfi_intelext_blockinfo BlockTypes[1];
+} __attribute__((packed));
+
+struct cfi_intelext_programming_regioninfo {
+	uint8_t  ProgRegShift;
+	uint8_t  Reserved1;
+	uint8_t  ControlValid;
+	uint8_t  Reserved2;
+	uint8_t  ControlInvalid;
+	uint8_t  Reserved3;
 } __attribute__((packed));
 
 /* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
 
 struct cfi_pri_amdstd {
-  __u8  pri[3];
-  __u8  MajorVersion;
-  __u8  MinorVersion;
-  __u8  SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
-  __u8  EraseSuspend;
-  __u8  BlkProt;
-  __u8  TmpBlkUnprotect;
-  __u8  BlkProtUnprot;
-  __u8  SimultaneousOps;
-  __u8  BurstMode;
-  __u8  PageMode;
-  __u8  VppMin;
-  __u8  VppMax;
-  __u8  TopBottom;
+	uint8_t  pri[3];
+	uint8_t  MajorVersion;
+	uint8_t  MinorVersion;
+	uint8_t  SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
+	uint8_t  EraseSuspend;
+	uint8_t  BlkProt;
+	uint8_t  TmpBlkUnprotect;
+	uint8_t  BlkProtUnprot;
+	uint8_t  SimultaneousOps;
+	uint8_t  BurstMode;
+	uint8_t  PageMode;
+	uint8_t  VppMin;
+	uint8_t  VppMax;
+	uint8_t  TopBottom;
 } __attribute__((packed));
 
 struct cfi_pri_query {
-  __u8  NumFields;
-  __u32 ProtField[1]; /* Not host ordered */
+	uint8_t  NumFields;
+	uint32_t ProtField[1]; /* Not host ordered */
 } __attribute__((packed));
 
 struct cfi_bri_query {
-  __u8  PageModeReadCap;
-  __u8  NumFields;
-  __u32 ConfField[1]; /* Not host ordered */
+	uint8_t  PageModeReadCap;
+	uint8_t  NumFields;
+	uint32_t ConfField[1]; /* Not host ordered */
 } __attribute__((packed));
 
-#define P_ID_NONE 0
-#define P_ID_INTEL_EXT 1
-#define P_ID_AMD_STD 2
-#define P_ID_INTEL_STD 3
-#define P_ID_AMD_EXT 4
-#define P_ID_ST_ADV 32
-#define P_ID_MITSUBISHI_STD 256
-#define P_ID_MITSUBISHI_EXT 257
-#define P_ID_SST_PAGE 258
-#define P_ID_RESERVED 65535
+#define P_ID_NONE               0x0000
+#define P_ID_INTEL_EXT          0x0001
+#define P_ID_AMD_STD            0x0002
+#define P_ID_INTEL_STD          0x0003
+#define P_ID_AMD_EXT            0x0004
+#define P_ID_WINBOND            0x0006
+#define P_ID_ST_ADV             0x0020
+#define P_ID_MITSUBISHI_STD     0x0100
+#define P_ID_MITSUBISHI_EXT     0x0101
+#define P_ID_SST_PAGE           0x0102
+#define P_ID_INTEL_PERFORMANCE  0x0200
+#define P_ID_INTEL_DATA         0x0210
+#define P_ID_RESERVED           0xffff
 
 
 #define CFI_MODE_CFI	1
 #define CFI_MODE_JEDEC	0
 
 struct cfi_private {
-	__u16 cmdset;
+	uint16_t cmdset;
 	void *cmdset_priv;
 	int interleave;
 	int device_type;
@@ -339,103 +251,142 @@
 /*
  * Returns the command address according to the given geometry.
  */
-static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
+static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
 {
 	return (cmd_ofs * type) * interleave;
 }
 
 /*
- * Transforms the CFI command for the given geometry (bus width & interleave.
+ * Transforms the CFI command for the given geometry (bus width & interleave).
+ * It looks too long to be inline, but in the common case it should almost all
+ * get optimised away. 
  */
-static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
+static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
 {
-	cfi_word val = 0;
+	map_word val = { {0} };
+	int wordwidth, words_per_bus, chip_mode, chips_per_word;
+	unsigned long onecmd;
+	int i;
+
+	/* We do it this way to give the compiler a fighting chance 
+	   of optimising away all the crap for 'bankwidth' larger than
+	   an unsigned long, in the common case where that support is
+	   disabled */
+	if (map_bankwidth_is_large(map)) {
+		wordwidth = sizeof(unsigned long);
+		words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
+	} else {
+		wordwidth = map_bankwidth(map);
+		words_per_bus = 1;
+	}
+	
+	chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
+	chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+
+	/* First, determine what the bit-pattern should be for a single
+	   device, according to chip mode and endianness... */
+	switch (chip_mode) {
+	default: BUG();
+	case 1:
+		onecmd = cmd;
+		break;
+	case 2:
+		onecmd = cpu_to_cfi16(cmd);
+		break;
+	case 4:
+		onecmd = cpu_to_cfi32(cmd);
+		break;
+	}
+
+	/* Now replicate it across the size of an unsigned long, or 
+	   just to the bus width as appropriate */
+	switch (chips_per_word) {
+	default: BUG();
+#if BITS_PER_LONG >= 64
+	case 8:
+		onecmd |= (onecmd << (chip_mode * 32));
+#endif
+	case 4:
+		onecmd |= (onecmd << (chip_mode * 16));
+	case 2:
+		onecmd |= (onecmd << (chip_mode * 8));
+	case 1:
+		;
+	}
 
-	if (cfi_buswidth_is_1()) {
-		/* 1 x8 device */
-		val = cmd;
-	} else if (cfi_buswidth_is_2()) {
-		if (cfi_interleave_is_1()) {
-			/* 1 x16 device in x16 mode */
-			val = cpu_to_cfi16(cmd);
-		} else if (cfi_interleave_is_2()) {
-			/* 2 (x8, x16 or x32) devices in x8 mode */
-			val = cpu_to_cfi16((cmd << 8) | cmd);
-		}
-	} else if (cfi_buswidth_is_4()) {
-		if (cfi_interleave_is_1()) {
-			/* 1 x32 device in x32 mode */
-			val = cpu_to_cfi32(cmd);
-		} else if (cfi_interleave_is_2()) {
-			/* 2 x16 device in x16 mode */
-			val = cpu_to_cfi32((cmd << 16) | cmd);
-		} else if (cfi_interleave_is_4()) {
-			/* 4 (x8, x16 or x32) devices in x8 mode */
-			val = (cmd << 16) | cmd;
-			val = cpu_to_cfi32((val << 8) | val);
-		}
-#ifdef CFI_WORD_64
-	} else if (cfi_buswidth_is_8()) {
-		if (cfi_interleave_is_1()) {
-			/* 1 x64 device in x64 mode */
-			val = cpu_to_cfi64(cmd);
-		} else if (cfi_interleave_is_2()) {
-			/* 2 x32 device in x32 mode */
-			val = cmd;
-			val = cpu_to_cfi64((val << 32) | val);
-		} else if (cfi_interleave_is_4()) {
-			/* 4 (x16, x32 or x64) devices in x16 mode */
-			val = (cmd << 16) | cmd;
-			val = cpu_to_cfi64((val << 32) | val);
-		} else if (cfi_interleave_is_8()) {
-			/* 8 (x8, x16 or x32) devices in x8 mode */
-			val = (cmd << 8) | cmd;
-			val = (val << 16) | val;
-			val = (val << 32) | val;
-			val = cpu_to_cfi64(val);
-		}
-#endif /* CFI_WORD_64 */
+	/* And finally, for the multi-word case, replicate it 
+	   in all words in the structure */
+	for (i=0; i < words_per_bus; i++) {
+		val.x[i] = onecmd;
 	}
+
 	return val;
 }
 #define CMD(x)  cfi_build_cmd((x), map, cfi)
 
-/*
- * Read a value according to the bus width.
- */
 
-static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
+static inline unsigned long cfi_merge_status(map_word val, struct map_info *map, 
+					   struct cfi_private *cfi)
 {
-	if (cfi_buswidth_is_1()) {
-		return map_read8(map, addr);
-	} else if (cfi_buswidth_is_2()) {
-		return map_read16(map, addr);
-	} else if (cfi_buswidth_is_4()) {
-		return map_read32(map, addr);
-	} else if (cfi_buswidth_is_8()) {
-		return map_read64(map, addr);
+	int wordwidth, words_per_bus, chip_mode, chips_per_word;
+	unsigned long onestat, res = 0;
+	int i;
+
+	/* We do it this way to give the compiler a fighting chance 
+	   of optimising away all the crap for 'bankwidth' larger than
+	   an unsigned long, in the common case where that support is
+	   disabled */
+	if (map_bankwidth_is_large(map)) {
+		wordwidth = sizeof(unsigned long);
+		words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
 	} else {
-		return 0;
+		wordwidth = map_bankwidth(map);
+		words_per_bus = 1;
+	}
+	
+	chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
+	chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+
+	onestat = val.x[0];
+	/* Or all status words together */
+	for (i=1; i < words_per_bus; i++) {
+		onestat |= val.x[i];
 	}
-}
 
-/*
- * Write a value according to the bus width.
- */
+	res = onestat;
+	switch(chips_per_word) {
+	default: BUG();
+#if BITS_PER_LONG >= 64
+	case 8:
+		res |= (onestat >> (chip_mode * 32));
+#endif
+	case 4:
+		res |= (onestat >> (chip_mode * 16));
+	case 2:
+		res |= (onestat >> (chip_mode * 8));
+	case 1:
+		;
+	}
 
-static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
-{
-	if (cfi_buswidth_is_1()) {
-		map_write8(map, val, addr);
-	} else if (cfi_buswidth_is_2()) {
-		map_write16(map, val, addr);
-	} else if (cfi_buswidth_is_4()) {
-		map_write32(map, val, addr);
-	} else if (cfi_buswidth_is_8()) {
-		map_write64(map, val, addr);
+	/* Last, determine what the bit-pattern should be for a single
+	   device, according to chip mode and endianness... */
+	switch (chip_mode) {
+	case 1:
+		break;
+	case 2:
+		res = cfi16_to_cpu(res);
+		break;
+	case 4:
+		res = cfi32_to_cpu(res);
+		break;
+	default: BUG();
 	}
+	return res;
 }
 
+#define MERGESTATUS(x) cfi_merge_status((x), map, cfi)
+
+
 /*
  * Sends a CFI command to a bank of flash for the given geometry.
  *
@@ -443,75 +394,71 @@
  * If prev_val is non-null, it will be set to the value at the command address,
  * before the command was written.
  */
-static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
+static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
 				struct map_info *map, struct cfi_private *cfi,
-				int type, cfi_word *prev_val)
+				int type, map_word *prev_val)
 {
-	cfi_word val;
-	__u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
+	map_word val;
+	uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
 
 	val = cfi_build_cmd(cmd, map, cfi);
 
 	if (prev_val)
-		*prev_val = cfi_read(map, addr);
+		*prev_val = map_read(map, addr);
 
-	cfi_write(map, val, addr);
+	map_write(map, val, addr);
 
 	return addr - base;
 }
 
-static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
+static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
 {
-	if (cfi_buswidth_is_1()) {
-		return map_read8(map, addr);
-	} else if (cfi_buswidth_is_2()) {
-		return cfi16_to_cpu(map_read16(map, addr));
-	} else if (cfi_buswidth_is_4()) {
-		return cfi32_to_cpu(map_read32(map, addr));
-	} else if (cfi_buswidth_is_8()) {
-		return cfi64_to_cpu(map_read64(map, addr));
+	map_word val = map_read(map, addr);
+
+	if (map_bankwidth_is_1(map)) {
+		return val.x[0];
+	} else if (map_bankwidth_is_2(map)) {
+		return cfi16_to_cpu(val.x[0]);
 	} else {
-		return 0;
+		/* No point in a 64-bit byteswap since that would just be
+		   swapping the responses from different chips, and we are
+		   only interested in one chip (a representative sample) */
+		return cfi32_to_cpu(val.x[0]);
 	}
 }
 
 static inline void cfi_udelay(int us)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-	unsigned long t = us * HZ / 1000000;
-	if (t) {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(t);
-		return;
+	if (us >= 1000) {
+		msleep((us+999)/1000);
+	} else {
+		udelay(us);
+		cond_resched();
 	}
-#endif
-	udelay(us);
-	cond_resched();
-}
-
-static inline void cfi_spin_lock(spinlock_t *mutex)
-{
-	spin_lock_bh(mutex);
-}
-
-static inline void cfi_spin_unlock(spinlock_t *mutex)
-{
-	spin_unlock_bh(mutex);
 }
 
-struct cfi_extquery *cfi_read_pri(struct map_info *map, __u16 adr, __u16 size,
+struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
 			     const char* name);
-
 struct cfi_fixup {
-	__u16 mfr;
-	__u16 id;
-	void (*fixup)(struct map_info *map, void* param);
+	uint16_t mfr;
+	uint16_t id;
+	void (*fixup)(struct mtd_info *mtd, void* param);
 	void* param;
 };
 
 #define CFI_MFR_ANY 0xffff
 #define CFI_ID_ANY  0xffff
 
-void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups);
+#define CFI_MFR_AMD 0x0001
+#define CFI_MFR_ST  0x0020 	/* STMicroelectronics */
+
+void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
+
+typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
+			      unsigned long adr, int len, void *thunk);
+
+int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
+	loff_t ofs, size_t len, void *thunk);
+
 
 #endif /* __MTD_CFI_H__ */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/cfi_endian.h linux-2.4.30/include/linux/mtd/cfi_endian.h
--- linux-2.4.30-orig/include/linux/mtd/cfi_endian.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/cfi_endian.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: cfi_endian.h,v 1.11 2002/01/30 23:20:48 awozniak Exp $
+ * $Id: cfi_endian.h,v 1.3 2005/10/12 14:52:46 lel Exp $
  *
  */
 
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/compatmac.h linux-2.4.30/include/linux/mtd/compatmac.h
--- linux-2.4.30-orig/include/linux/mtd/compatmac.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/compatmac.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: compatmac.h,v 1.63 2003/11/14 19:50:04 thayne Exp $
+ * $Id: compatmac.h,v 1.3 2005/10/12 14:52:46 lel Exp $
  *
  * Extensions and omissions from the normal 'linux/compatmac.h'
  * files. hopefully this will end up empty as the 'real' one 
@@ -27,7 +27,6 @@
 #define recalc_sigpending() __recalc_sigpending ()
 
 #define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
-
 #endif
 
 
@@ -123,9 +122,6 @@
 	return 0;
 }
 
-#undef sighand
-#undef sigmask_lock
-
 #define PF_FREEZE 0
 #define refrigerator(x) do { ; } while(0)
 #endif
@@ -186,4 +182,68 @@
 	(type *)( (char *)__mptr - offsetof(type,member) );})
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
+#define kvec iovec
+#define __user 
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,28)
+#define msleep(x) \
+	set_current_state(TASK_UNINTERRUPTIBLE); \
+	schedule_timeout((x)*(HZ/1000));
+#endif
+
+#ifndef __iomem
+#define __iomem
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#define CURRENT_TIME_SEC ((struct timespec) { xtime.tv_sec, 0 })
+#include <linux/sched.h>
+#include <linux/suspend.h>
+static inline int try_to_freeze(unsigned long refrigerator_flags)
+{
+	if (unlikely(current->flags & PF_FREEZE)) {
+	        refrigerator(refrigerator_flags);
+	        return 1;
+	} else
+	        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12)
+#define try_to_freeze() try_to_freeze(0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+typedef struct class_simple class;
+#define class_device_create(cs, dev, device, fmt, ...) class_simple_device_add((struct class_simple *)cs, dev, device, fmt, __VA_ARGS__)
+#define class_device_destroy(cs, dev) class_simple_device_remove(dev)
+#define class_create(owner, name) (struct class *)class_simple_create(owner, name)
+#define class_destroy(cs) class_simple_destroy((struct class_simple *)cs)
+#endif
+
+#ifndef list_for_each_entry_safe
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop counter.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif
+
+#ifndef DEFINE_SPINLOCK
+#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED
+#endif
+#ifndef DEFINE_RWLOCK
+#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED
+#endif
+
 #endif /* __LINUX_MTD_COMPATMAC_H__ */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/concat.h linux-2.4.30/include/linux/mtd/concat.h
--- linux-2.4.30-orig/include/linux/mtd/concat.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/concat.h	2006-12-11 18:18:37.000000000 +0100
@@ -5,7 +5,7 @@
  *
  * This code is GPL
  *
- * $Id: concat.h,v 1.1 2002/03/08 16:34:36 rkaiser Exp $
+ * $Id: concat.h,v 1.3 2005/10/12 14:52:46 lel Exp $
  */
 
 #ifndef MTD_CONCAT_H
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/doc2000.h linux-2.4.30/include/linux/mtd/doc2000.h
--- linux-2.4.30-orig/include/linux/mtd/doc2000.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/doc2000.h	2006-12-11 18:18:37.000000000 +0100
@@ -6,7 +6,7 @@
  * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
  * Copyright (C) 2002-2003 SnapGear Inc
  *
- * $Id: doc2000.h,v 1.22 2003/11/05 10:51:36 dwmw2 Exp $ 
+ * $Id: doc2000.h,v 1.3 2005/10/12 14:52:46 lel Exp $ 
  *
  * Released under GPL
  */
@@ -89,8 +89,8 @@
 #define WriteDOC_(d, adr, reg)  do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
 #define DOC_IOREMAP_LEN 0x4000
 #else
-#define ReadDOC_(adr, reg)      readb(((unsigned long)adr) + (reg))
-#define WriteDOC_(d, adr, reg)  writeb(d, ((unsigned long)adr) + (reg))
+#define ReadDOC_(adr, reg)      readb((void __iomem *)(adr) + (reg))
+#define WriteDOC_(d, adr, reg)  writeb(d, (void __iomem *)(adr) + (reg))
 #define DOC_IOREMAP_LEN 0x2000
 
 #endif
@@ -168,7 +168,7 @@
 
 struct DiskOnChip {
 	unsigned long physadr;
-	unsigned long virtadr;
+	void __iomem *virtadr;
 	unsigned long totlen;
 	unsigned char ChipID; /* Type of DiskOnChip */
 	int ioreg;
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/flashchip.h linux-2.4.30/include/linux/mtd/flashchip.h
--- linux-2.4.30-orig/include/linux/mtd/flashchip.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/flashchip.h	2006-12-11 18:18:37.000000000 +0100
@@ -6,7 +6,7 @@
  *
  * (C) 2000 Red Hat. GPLd.
  *
- * $Id: flashchip.h,v 1.10 2004/01/27 10:16:20 dvrabel Exp $
+ * $Id: flashchip.h,v 1.3 2005/10/12 14:52:46 lel Exp $
  *
  */
 
@@ -29,6 +29,7 @@
 	FL_ERASE_SUSPENDED,
 	FL_WRITING,
 	FL_WRITING_TO_BUFFER,
+	FL_OTP_WRITE,
 	FL_WRITE_SUSPENDING,
 	FL_WRITE_SUSPENDED,
 	FL_PM_SUSPENDED,
@@ -37,13 +38,16 @@
 	FL_LOCKING,
 	FL_UNLOCKING,
 	FL_POINT,
+	FL_XIP_WHILE_ERASING,
+	FL_XIP_WHILE_WRITING,
 	FL_UNKNOWN
 } flstate_t;
 
 
 
 /* NOTE: confusingly, this can be used to refer to more than one chip at a time, 
-   if they're interleaved. */
+   if they're interleaved.  This can even refer to individual partitions on
+   the same physical chip when present. */
 
 struct flchip {
 	unsigned long start; /* Offset within the map */
@@ -59,8 +63,8 @@
 	flstate_t state;
 	flstate_t oldstate;
 
-	int write_suspended:1;
-	int erase_suspended:1;
+	unsigned int write_suspended:1;
+	unsigned int erase_suspended:1;
 	unsigned long in_progress_block_addr;
 
 	spinlock_t *mutex;
@@ -70,8 +74,17 @@
 	int word_write_time;
 	int buffer_write_time;
 	int erase_time;
+
+	void *priv;
 };
 
+/* This is used to handle contention on write/erase operations
+   between partitions of the same physical chip. */
+struct flchip_shared {
+	spinlock_t lock;
+	struct flchip *writing;
+	struct flchip *erasing;
+};
 
 
 #endif /* __MTD_FLASHCHIP_H__ */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/ftl.h linux-2.4.30/include/linux/mtd/ftl.h
--- linux-2.4.30-orig/include/linux/mtd/ftl.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/ftl.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: ftl.h,v 1.6 2003/01/24 13:20:04 dwmw2 Exp $
+ * $Id: ftl.h,v 1.3 2005/10/12 14:52:46 lel Exp $
  * 
  * Derived from (and probably identical to):
  * ftl.h 1.7 1999/10/25 20:23:17
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/gen_probe.h linux-2.4.30/include/linux/mtd/gen_probe.h
--- linux-2.4.30-orig/include/linux/mtd/gen_probe.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/gen_probe.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,7 +1,7 @@
 /*
  * (C) 2001, 2001 Red Hat, Inc.
  * GPL'd
- * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $
+ * $Id: gen_probe.h,v 1.3 2005/10/12 14:52:46 lel Exp $
  */
 
 #ifndef __LINUX_MTD_GEN_PROBE_H__
@@ -10,7 +10,7 @@
 #include <linux/mtd/flashchip.h>
 #include <linux/mtd/map.h> 
 #include <linux/mtd/cfi.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
 
 struct chip_probe {
 	char *name;
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/iflash.h linux-2.4.30/include/linux/mtd/iflash.h
--- linux-2.4.30-orig/include/linux/mtd/iflash.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/iflash.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,4 +1,4 @@
-/* $Id: iflash.h,v 1.2 2000/11/13 18:01:54 dwmw2 Exp $ */
+/* $Id: iflash.h,v 1.3 2005/10/12 14:52:46 lel Exp $ */
 
 #ifndef __MTD_IFLASH_H__
 #define __MTD_IFLASH_H__
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/inftl.h linux-2.4.30/include/linux/mtd/inftl.h
--- linux-2.4.30-orig/include/linux/mtd/inftl.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/inftl.h	2006-12-11 18:18:37.000000000 +0100
@@ -3,7 +3,7 @@
  *
  *	(C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
  *
- *	$Id: inftl.h,v 1.5 2004/05/05 15:16:59 dwmw2 Exp $
+ *	$Id: inftl.h,v 1.3 2005/10/12 14:52:47 lel Exp $
  */
 
 #ifndef __MTD_INFTL_H__
@@ -20,7 +20,7 @@
 #include <mtd/inftl-user.h>
 
 #ifndef INFTL_MAJOR
-#define INFTL_MAJOR 94
+#define INFTL_MAJOR 96
 #endif
 #define INFTL_PARTN_BITS 4
 
@@ -28,7 +28,7 @@
 
 struct INFTLrecord {
 	struct mtd_blktrans_dev mbd;
-	__u16 MediaUnit, SpareMediaUnit;
+	__u16 MediaUnit;
 	__u32 EraseSize;
 	struct INFTLMediaHeader MediaHdr;
 	int usecount;
@@ -46,6 +46,7 @@
         unsigned int nb_blocks;		/* number of physical blocks */
         unsigned int nb_boot_blocks;	/* number of blocks used by the bios */
         struct erase_info instr;
+        struct nand_oobinfo oobinfo;
 };
 
 int INFTL_mount(struct INFTLrecord *s);
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/jedec.h linux-2.4.30/include/linux/mtd/jedec.h
--- linux-2.4.30-orig/include/linux/mtd/jedec.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/jedec.h	2006-12-11 18:18:37.000000000 +0100
@@ -7,7 +7,7 @@
  *
  * See the AMD flash databook for information on how to operate the interface.
  *
- * $Id: jedec.h,v 1.3 2003/05/21 11:51:01 dwmw2 Exp $
+ * $Id: jedec.h,v 1.3 2005/10/12 14:52:47 lel Exp $
  */
 
 #ifndef __LINUX_MTD_JEDEC_H__
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/map.h linux-2.4.30/include/linux/mtd/map.h
--- linux-2.4.30-orig/include/linux/mtd/map.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/map.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,6 +1,6 @@
 
 /* Overhauled routines for dealing with different mmap regions of flash */
-/* $Id: map.h,v 1.35 2004/04/02 01:25:24 sjhill Exp $ */
+/* $Id: map.h,v 1.3 2005/10/12 14:52:47 lel Exp $ */
 
 #ifndef __LINUX_MTD_MAP_H__
 #define __LINUX_MTD_MAP_H__
@@ -8,17 +8,169 @@
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/mtd/compatmac.h>
+#include <asm/unaligned.h>
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/bugs.h>
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
+#define map_bankwidth(map) 1
+#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
+#define map_bankwidth_is_large(map) (0)
+#define map_words(map) (1)
+#define MAX_MAP_BANKWIDTH 1
+#else
+#define map_bankwidth_is_1(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+# else
+#  define map_bankwidth(map) 2
+#  define map_bankwidth_is_large(map) (0)
+#  define map_words(map) (1)
+# endif
+#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 2
+#else
+#define map_bankwidth_is_2(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+# else
+#  define map_bankwidth(map) 4
+#  define map_bankwidth_is_large(map) (0)
+#  define map_words(map) (1)
+# endif
+#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 4
+#else
+#define map_bankwidth_is_4(map) (0)
+#endif
+
+/* ensure we never evaluate anything shorted than an unsigned long
+ * to zero, and ensure we'll never miss the end of an comparison (bjd) */
+
+#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+#  if BITS_PER_LONG < 64
+#   undef map_bankwidth_is_large
+#   define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+#   undef map_words
+#   define map_words(map) map_calc_words(map)
+#  endif
+# else
+#  define map_bankwidth(map) 8
+#  define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
+#  define map_words(map) map_calc_words(map)
+# endif
+#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 8
+#else
+#define map_bankwidth_is_8(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+#  undef map_bankwidth_is_large
+#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+#  undef map_words
+#  define map_words(map) map_calc_words(map)
+# else
+#  define map_bankwidth(map) 16
+#  define map_bankwidth_is_large(map) (1)
+#  define map_words(map) map_calc_words(map)
+# endif
+#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 16
+#else
+#define map_bankwidth_is_16(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+#  undef map_bankwidth_is_large
+#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+#  undef map_words
+#  define map_words(map) map_calc_words(map)
+# else
+#  define map_bankwidth(map) 32
+#  define map_bankwidth_is_large(map) (1)
+#  define map_words(map) map_calc_words(map)
+# endif
+#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 32
+#else
+#define map_bankwidth_is_32(map) (0)
+#endif
+
+#ifndef map_bankwidth
+#error "No bus width supported. What's the point?"
+#endif
+
+static inline int map_bankwidth_supported(int w)
+{
+	switch (w) {
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
+	case 1:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
+	case 2:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
+	case 4:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
+	case 8:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
+	case 16:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
+	case 32:
+#endif
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
+
+typedef union {
+	unsigned long x[MAX_MAP_LONGS];
+} map_word;
 
 /* The map stuff is very simple. You fill in your struct map_info with
    a handful of routines for accessing the device, making sure they handle
    paging etc. correctly if your device needs it. Then you pass it off
-   to a chip driver which deals with a mapped device - generally either
-   do_cfi_probe() or do_ram_probe(), either of which will return a 
-   struct mtd_info if they liked what they saw. At which point, you
-   fill in the mtd->module with your own module address, and register 
-   it.
+   to a chip probe routine -- either JEDEC or CFI probe or both -- via
+   do_map_probe(). If a chip is recognised, the probe code will invoke the
+   appropriate chip driver (if present) and return a struct mtd_info.
+   At which point, you fill in the mtd->module with your own module 
+   address, and register it with the MTD core code. Or you could partition
+   it and register the partitions instead, or keep it for your own private
+   use; whatever.
    
    The mtd->priv field will point to the struct map_info, and any further
    private data required by the chip driver is linked from the 
@@ -33,31 +185,32 @@
 	unsigned long phys;
 #define NO_XIP (-1UL)
 
-	unsigned long virt;
+	void __iomem *virt;
 	void *cached;
 
-	int buswidth; /* in octets */
+	int bankwidth; /* in octets. This isn't necessarily the width
+		       of actual bus cycles -- it's the repeat interval
+		      in bytes, before you are talking to the first chip again.
+		      */
 
 #ifdef CONFIG_MTD_COMPLEX_MAPPINGS
-	u8 (*read8)(struct map_info *, unsigned long);
-	u16 (*read16)(struct map_info *, unsigned long);
-	u32 (*read32)(struct map_info *, unsigned long);  
-	u64 (*read64)(struct map_info *, unsigned long);  
-	/* If it returned a 'long' I'd call it readl.
-	 * It doesn't.
-	 * I won't.
-	 * dwmw2 */
-	
+	map_word (*read)(struct map_info *, unsigned long);
 	void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
-	void (*write8)(struct map_info *, u8, unsigned long);
-	void (*write16)(struct map_info *, u16, unsigned long);
-	void (*write32)(struct map_info *, u32, unsigned long);
-	void (*write64)(struct map_info *, u64, unsigned long);
+
+	void (*write)(struct map_info *, const map_word, unsigned long);
 	void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
 
 	/* We can perhaps put in 'point' and 'unpoint' methods, if we really
 	   want to enable XIP for non-linear mappings. Not yet though. */
 #endif
+	/* It's possible for the map driver to use cached memory in its
+	   copy_from implementation (and _only_ with copy_from).  However,
+	   when the chip driver knows some flash area has changed contents,
+	   it will signal it to the map driver through this routine to let
+	   the map driver invalidate the corresponding cache as needed.
+	   If there is no cache to care about this can be set to NULL. */
+	void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
+
 	/* set_vpp() must handle being reentered -- enable, enable, disable 
 	   must leave it enabled. */
 	void (*set_vpp)(struct map_info *, int);
@@ -85,87 +238,197 @@
 #define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
 #define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
 
-#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
-#define map_read8(map, ofs) (map)->read8(map, ofs)
-#define map_read16(map, ofs) (map)->read16(map, ofs)
-#define map_read32(map, ofs) (map)->read32(map, ofs)
-#define map_read64(map, ofs) (map)->read64(map, ofs)
-#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
-#define map_write8(map, datum, ofs) (map)->write8(map, datum, ofs)
-#define map_write16(map, datum, ofs) (map)->write16(map, datum, ofs)
-#define map_write32(map, datum, ofs) (map)->write32(map, datum, ofs)
-#define map_write64(map, datum, ofs) (map)->write64(map, datum, ofs)
-#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
+#define INVALIDATE_CACHED_RANGE(map, from, size) \
+	do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
 
-extern void simple_map_init(struct map_info *);
-#define map_is_linear(map) (map->phys != NO_XIP)
 
-#else
-static inline u8 map_read8(struct map_info *map, unsigned long ofs)
+static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
+{
+	int i;
+	for (i=0; i<map_words(map); i++) {
+		if (val1.x[i] != val2.x[i])
+			return 0;
+	}
+	return 1;
+}
+
+static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2)
 {
-	return __raw_readb(map->virt + ofs);
+	map_word r;
+	int i;
+
+	for (i=0; i<map_words(map); i++) {
+		r.x[i] = val1.x[i] & val2.x[i];
+	}
+	return r;
 }
 
-static inline u16 map_read16(struct map_info *map, unsigned long ofs)
+static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2)
 {
-	return __raw_readw(map->virt + ofs);
+	map_word r;
+	int i;
+
+	for (i=0; i<map_words(map); i++) {
+		r.x[i] = val1.x[i] & ~val2.x[i];
+	}
+	return r;
 }
 
-static inline u32 map_read32(struct map_info *map, unsigned long ofs)
+static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
 {
-	return __raw_readl(map->virt + ofs);
+	map_word r;
+	int i;
+
+	for (i=0; i<map_words(map); i++) {
+		r.x[i] = val1.x[i] | val2.x[i];
+	}
+	return r;
 }
 
-static inline u64 map_read64(struct map_info *map, unsigned long ofs)
+#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
+
+static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
 {
-#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
-	BUG();
+	int i;
+
+	for (i=0; i<map_words(map); i++) {
+		if (val1.x[i] & val2.x[i])
+			return 1;
+	}
 	return 0;
-#else
-	return __raw_readq(map->virt + ofs);
+}
+
+static inline map_word map_word_load(struct map_info *map, const void *ptr)
+{
+	map_word r;
+
+	if (map_bankwidth_is_1(map))
+		r.x[0] = *(unsigned char *)ptr;
+	else if (map_bankwidth_is_2(map))
+		r.x[0] = get_unaligned((uint16_t *)ptr);
+	else if (map_bankwidth_is_4(map))
+		r.x[0] = get_unaligned((uint32_t *)ptr);
+#if BITS_PER_LONG >= 64
+	else if (map_bankwidth_is_8(map))
+		r.x[0] = get_unaligned((uint64_t *)ptr);
 #endif
+	else if (map_bankwidth_is_large(map))
+		memcpy(r.x, ptr, map->bankwidth);
+
+	return r;
 }
 
-static inline void map_write8(struct map_info *map, u8 datum, unsigned long ofs)
+static inline map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len)
 {
-	__raw_writeb(datum, map->virt + ofs);
-	mb();
+	int i;
+
+	if (map_bankwidth_is_large(map)) {
+		char *dest = (char *)&orig;
+		memcpy(dest+start, buf, len);
+	} else {
+		for (i=start; i < start+len; i++) {
+			int bitpos;
+#ifdef __LITTLE_ENDIAN
+			bitpos = i*8;
+#else /* __BIG_ENDIAN */
+			bitpos = (map_bankwidth(map)-1-i)*8;
+#endif
+			orig.x[0] &= ~(0xff << bitpos);
+			orig.x[0] |= buf[i-start] << bitpos;
+		}
+	}
+	return orig;
 }
 
-static inline void map_write16(struct map_info *map, u16 datum, unsigned long ofs)
+#if BITS_PER_LONG < 64
+#define MAP_FF_LIMIT 4
+#else
+#define MAP_FF_LIMIT 8
+#endif
+
+static inline map_word map_word_ff(struct map_info *map)
 {
-	__raw_writew(datum, map->virt + ofs);
-	mb();
+	map_word r;
+	int i;
+	
+	if (map_bankwidth(map) < MAP_FF_LIMIT) {
+		int bw = 8 * map_bankwidth(map);
+		r.x[0] = (1 << bw) - 1;
+	} else {
+		for (i=0; i<map_words(map); i++)
+			r.x[i] = ~0UL;
+	}
+	return r;
 }
 
-static inline void map_write32(struct map_info *map, u32 datum, unsigned long ofs)
+static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
 {
-	__raw_writel(datum, map->virt + ofs);
-	mb();
+	map_word r;
+
+	if (map_bankwidth_is_1(map))
+		r.x[0] = __raw_readb(map->virt + ofs);
+	else if (map_bankwidth_is_2(map))
+		r.x[0] = __raw_readw(map->virt + ofs);
+	else if (map_bankwidth_is_4(map))
+		r.x[0] = __raw_readl(map->virt + ofs);
+#if BITS_PER_LONG >= 64
+	else if (map_bankwidth_is_8(map))
+		r.x[0] = __raw_readq(map->virt + ofs);
+#endif
+	else if (map_bankwidth_is_large(map))
+		memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
+
+	return r;
 }
 
-static inline void map_write64(struct map_info *map, u64 datum, unsigned long ofs)
+static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
 {
-#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
-	BUG();
-#else
-	__raw_writeq(datum, map->virt + ofs);
+	if (map_bankwidth_is_1(map))
+		__raw_writeb(datum.x[0], map->virt + ofs);
+	else if (map_bankwidth_is_2(map))
+		__raw_writew(datum.x[0], map->virt + ofs);
+	else if (map_bankwidth_is_4(map))
+		__raw_writel(datum.x[0], map->virt + ofs);
+#if BITS_PER_LONG >= 64
+	else if (map_bankwidth_is_8(map))
+		__raw_writeq(datum.x[0], map->virt + ofs);
+#endif
+	else if (map_bankwidth_is_large(map))
+		memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
 	mb();
-#endif /* CFI_B8 */
 }
 
-static inline void map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
 {
-	memcpy_fromio(to, map->virt + from, len);
+	if (map->cached)
+		memcpy(to, (char *)map->cached + from, len);
+	else
+		memcpy_fromio(to, map->virt + from, len);
 }
 
-static inline void map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
 {
 	memcpy_toio(map->virt + to, from, len);
 }
 
-#define simple_map_init(map) do { } while (0)
-#define map_is_linear(map) (1)
+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
+#define map_read(map, ofs) (map)->read(map, ofs)
+#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
+#define map_write(map, datum, ofs) (map)->write(map, datum, ofs)
+#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
+
+extern void simple_map_init(struct map_info *);
+#define map_is_linear(map) (map->phys != NO_XIP)
+
+#else
+#define map_read(map, ofs) inline_map_read(map, ofs)
+#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
+#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
+#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
+
+
+#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
+#define map_is_linear(map) ({ (void)(map); 1; })
 
 #endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
 
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/mtd.h linux-2.4.30/include/linux/mtd/mtd.h
--- linux-2.4.30-orig/include/linux/mtd/mtd.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/mtd.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /* 
- * $Id: mtd.h,v 1.51 2004/05/05 14:44:56 dwmw2 Exp $
+ * $Id: mtd.h,v 1.3 2005/10/12 14:52:47 lel Exp $
  *
  * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
  *
@@ -18,7 +18,9 @@
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/uio.h>
+#include <linux/notifier.h>
 
+#include <linux/mtd/compatmac.h>
 #include <mtd/mtd-abi.h>
 
 #define MTD_CHAR_MAJOR 90
@@ -31,10 +33,14 @@
 #define MTD_ERASE_DONE          0x08
 #define MTD_ERASE_FAILED        0x10
 
+/* If the erase fails, fail_addr might indicate exactly which block failed.  If
+   fail_addr = 0xffffffff, the failure was not at the device level or was not
+   specific to any particular block. */
 struct erase_info {
 	struct mtd_info *mtd;
 	u_int32_t addr;
 	u_int32_t len;
+	u_int32_t fail_addr;
 	u_long time;
 	u_long retries;
 	u_int dev;
@@ -64,10 +70,19 @@
 
 	u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
 	u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
-	u_int32_t oobavail;  // Number of bytes in OOB area available for fs 
 	u_int32_t ecctype;
 	u_int32_t eccsize;
-	
+
+	/*
+	 * Reuse some of the above unused fields in the case of NOR flash
+	 * with configurable programming regions to avoid modifying the
+	 * user visible structure layout/size.  Only valid when the
+	 * MTD_PROGRAM_REGIONS flag is set.
+	 * (Maybe we should have an union for those?)
+	 */
+#define MTD_PROGREGION_SIZE(mtd)  (mtd)->oobblock
+#define MTD_PROGREGION_CTRLMODE_VALID(mtd)  (mtd)->oobsize
+#define MTD_PROGREGION_CTRLMODE_INVALID(mtd)  (mtd)->ecctype
 
 	// Kernel-only stuff starts here.
 	char *name;
@@ -75,6 +90,7 @@
 
 	// oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO)
 	struct nand_oobinfo oobinfo;
+	u_int32_t oobavail;  // Number of bytes in OOB area available for fs 
 
 	/* Data for variable erase regions. If numeraseregions is zero,
 	 * it means that the whole device has erasesize as given above. 
@@ -108,23 +124,23 @@
 	 * flash devices. The user data is one time programmable but the
 	 * factory data is read only. 
 	 */
-	int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-
+	int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
 	int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-
-	/* This function is not yet implemented */
+	int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
+	int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
 	int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+	int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
 
-	/* iovec-based read/write methods. We need these especially for NAND flash,
+	/* kvec-based read/write methods. We need these especially for NAND flash,
 	   with its limited number of write cycles per erase.
 	   NB: The 'count' parameter is the number of _vectors_, each of 
 	   which contains an (ofs, len) tuple.
 	*/
-	int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
-	int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, 
+	int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
+	int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, 
 		size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
-	int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
-	int (*writev_ecc) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, 
+	int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
+	int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, 
 		size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
 
 	/* Sync */
@@ -142,6 +158,8 @@
 	int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
 	int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
 
+	struct notifier_block reboot_notifier;  /* default mode before reboot */
+
 	void *priv;
 
 	struct module *owner;
@@ -169,10 +187,10 @@
 extern void register_mtd_user (struct mtd_notifier *new);
 extern int unregister_mtd_user (struct mtd_notifier *old);
 
-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
+int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
 		       unsigned long count, loff_t to, size_t *retlen);
 
-int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
+int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
 		      unsigned long count, loff_t from, size_t *retlen);
 
 #define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
@@ -188,6 +206,17 @@
 #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
 #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd);  } while (0) 
 
+
+#ifdef CONFIG_MTD_PARTITIONS
+void mtd_erase_callback(struct erase_info *instr);
+#else
+static inline void mtd_erase_callback(struct erase_info *instr)
+{
+	if (instr->callback)
+		instr->callback(instr);
+}
+#endif
+
 /*
  * Debugging macro and defines
  */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/nand.h linux-2.4.30/include/linux/mtd/nand.h
--- linux-2.4.30-orig/include/linux/mtd/nand.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/nand.h	2006-12-11 18:18:37.000000000 +0100
@@ -5,7 +5,7 @@
  *                     Steven J. Hill <sjhill@realitydiluted.com>
  *		       Thomas Gleixner <tglx@linutronix.de>
  *
- * $Id: nand.h,v 1.46 2004/05/06 08:11:32 gleixner Exp $
+ * $Id: nand.h,v 1.3 2005/10/12 14:52:47 lel Exp $
  *
  * 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
@@ -46,6 +46,12 @@
  *			Split manufacturer and device ID structures 
  *
  *  02-08-2004 tglx 	added option field to nand structure for chip anomalities
+ *  05-25-2004 tglx 	added bad block table support, ST-MICRO manufacturer id
+ *			update of nand_chip structure description
+ *  01-17-2005 dmarlin	added extended commands for AG-AND device and added option 
+ * 			for BBT_AUTO_REFRESH.
+ *  01-20-2005 dmarlin	added optional pointer to hardware specific callback for 
+ *			extra error status checks.
  */
 #ifndef __LINUX_MTD_NAND_H
 #define __LINUX_MTD_NAND_H
@@ -53,12 +59,20 @@
 #include <linux/config.h>
 #include <linux/wait.h>
 #include <linux/spinlock.h>
+#include <linux/mtd/mtd.h>
 
 struct mtd_info;
-/*
- * Searches for a NAND device
- */
+/* Scan and identify a NAND device */
 extern int nand_scan (struct mtd_info *mtd, int max_chips);
+/* Free resources held by the NAND device */
+extern void nand_release (struct mtd_info *mtd);
+
+/* Read raw data from the device without ECC */
+extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
+
+
+/* The maximum number of NAND chips in an array */
+#define NAND_MAX_CHIPS		8
 
 /* This constant declares the max. oobsize / page, which
  * is supported now. If you add a chip with bigger oobsize/page
@@ -69,13 +83,21 @@
 /*
  * Constants for hardware specific CLE/ALE/NCE function
 */
+/* Select the chip by setting nCE to low */
 #define NAND_CTL_SETNCE 	1
+/* Deselect the chip by setting nCE to high */
 #define NAND_CTL_CLRNCE		2
+/* Select the command latch by setting CLE to high */
 #define NAND_CTL_SETCLE		3
+/* Deselect the command latch by setting CLE to low */
 #define NAND_CTL_CLRCLE		4
+/* Select the address latch by setting ALE to high */
 #define NAND_CTL_SETALE		5
+/* Deselect the address latch by setting ALE to low */
 #define NAND_CTL_CLRALE		6
+/* Set write protection by setting WP to high. Not used! */
 #define NAND_CTL_SETWP		7
+/* Clear write protection by setting WP to low. Not used! */
 #define NAND_CTL_CLRWP		8
 
 /*
@@ -87,6 +109,7 @@
 #define NAND_CMD_READOOB	0x50
 #define NAND_CMD_ERASE1		0x60
 #define NAND_CMD_STATUS		0x70
+#define NAND_CMD_STATUS_MULTI	0x71
 #define NAND_CMD_SEQIN		0x80
 #define NAND_CMD_READID		0x90
 #define NAND_CMD_ERASE2		0xd0
@@ -96,6 +119,25 @@
 #define NAND_CMD_READSTART	0x30
 #define NAND_CMD_CACHEDPROG	0x15
 
+/* Extended commands for AG-AND device */
+/* 
+ * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but 
+ *       there is no way to distinguish that from NAND_CMD_READ0
+ *       until the remaining sequence of commands has been completed
+ *       so add a high order bit and mask it off in the command.
+ */
+#define NAND_CMD_DEPLETE1	0x100
+#define NAND_CMD_DEPLETE2	0x38
+#define NAND_CMD_STATUS_MULTI	0x71
+#define NAND_CMD_STATUS_ERROR	0x72
+/* multi-bank error status (banks 0-3) */
+#define NAND_CMD_STATUS_ERROR0	0x73
+#define NAND_CMD_STATUS_ERROR1	0x74
+#define NAND_CMD_STATUS_ERROR2	0x75
+#define NAND_CMD_STATUS_ERROR3	0x76
+#define NAND_CMD_STATUS_RESET	0x7f
+#define NAND_CMD_STATUS_CLEAR	0xff
+
 /* Status bits */
 #define NAND_STATUS_FAIL	0x01
 #define NAND_STATUS_FAIL_N1	0x02
@@ -105,26 +147,36 @@
 
 /* 
  * Constants for ECC_MODES
- *
- * NONE:	No ECC
- * SOFT:	Software ECC 3 byte ECC per 256 Byte data
- * HW3_256:	Hardware ECC 3 byte ECC per 256 Byte data
- * HW3_512:	Hardware ECC 3 byte ECC per 512 Byte data
- *
- *
-*/
+ */
+
+/* No ECC. Usage is not recommended ! */
 #define NAND_ECC_NONE		0
+/* Software ECC 3 byte ECC per 256 Byte data */
 #define NAND_ECC_SOFT		1
+/* Hardware ECC 3 byte ECC per 256 Byte data */
 #define NAND_ECC_HW3_256	2
+/* Hardware ECC 3 byte ECC per 512 Byte data */
 #define NAND_ECC_HW3_512	3
+/* Hardware ECC 3 byte ECC per 512 Byte data */
 #define NAND_ECC_HW6_512	4
-#define NAND_ECC_DISKONCHIP	5
+/* Hardware ECC 8 byte ECC per 512 Byte data */
+#define NAND_ECC_HW8_512	6
+/* Hardware ECC 12 byte ECC per 2048 Byte data */
+#define NAND_ECC_HW12_2048	7
 
 /*
  * Constants for Hardware ECC
-*/
+ */
+/* Reset Hardware ECC for read */
 #define NAND_ECC_READ		0
+/* Reset Hardware ECC for write */
 #define NAND_ECC_WRITE		1
+/* Enable Hardware ECC before syndrom is read back from flash */
+#define NAND_ECC_READSYN	2
+
+/* Bit mask for flags passed to do_nand_read_ecc */
+#define NAND_GET_DEVICE		0x80
+
 
 /* Option constants for bizarre disfunctionality and real
 *  features
@@ -139,6 +191,16 @@
 #define NAND_CACHEPRG		0x00000008
 /* Chip has copy back function */
 #define NAND_COPYBACK		0x00000010
+/* AND Chip which has 4 banks and a confusing page / block 
+ * assignment. See Renesas datasheet for further information */
+#define NAND_IS_AND		0x00000020
+/* Chip has a array of 4 pages which can be read without
+ * additional ready /busy waits */
+#define NAND_4PAGE_ARRAY	0x00000040 
+/* Chip requires that BBT is periodically rewritten to prevent
+ * bits from adjacent blocks from 'leaking' in altering data.
+ * This happens with the Renesas AG-AND chips, possibly others.  */
+#define BBT_AUTO_REFRESH	0x00000080
 
 /* Options valid for Samsung large page devices */
 #define NAND_SAMSUNG_LP_OPTIONS \
@@ -150,7 +212,29 @@
 #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
 #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
 
+/* Mask to zero out the chip options, which come from the id table */
+#define NAND_CHIPOPTIONS_MSK	(0x0000ffff & ~NAND_NO_AUTOINCR)
+
+/* Non chip related options */
+/* Use a flash based bad block table. This option is passed to the
+ * default bad block table function. */
+#define NAND_USE_FLASH_BBT	0x00010000
+/* The hw ecc generator provides a syndrome instead a ecc value on read 
+ * This can only work if we have the ecc bytes directly behind the 
+ * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
+#define NAND_HWECC_SYNDROME	0x00020000
+/* This option skips the bbt scan during initialization. */
+#define NAND_SKIP_BBTSCAN	0x00040000
+
+/* Options set by nand scan */
+/* Nand scan has allocated oob_buf */
+#define NAND_OOBBUF_ALLOC	0x40000000
+/* Nand scan has allocated data_buf */
+#define NAND_DATABUF_ALLOC	0x80000000
+
+
 /*
+ * nand_state_t - chip states
  * Enumeration for NAND flash chip state
  */
 typedef enum {
@@ -160,54 +244,88 @@
 	FL_ERASING,
 	FL_SYNCING,
 	FL_CACHEDPRG,
+	FL_PM_SUSPENDED,
 } nand_state_t;
 
+/* Keep gcc happy */
+struct nand_chip;
 
-/*
- * NAND Private Flash Chip Data
- *
- * Structure overview:
- *
- *  IO_ADDR_R - address to read the 8 I/O lines of the flash device 
- *
- *  IO_ADDR_W - address to write the 8 I/O lines of the flash device 
- *
- *  hwcontrol - hardwarespecific function for accesing control-lines
- *
- *  dev_ready - hardwarespecific function for accesing device ready/busy line
- *
- *  waitfunc - hardwarespecific function for wait on ready
- *
- *  calculate_ecc - function for ecc calculation or readback from ecc hardware
- *
- *  correct_data - function for ecc correction, matching to ecc generator (sw/hw)
- *
- *  enable_hwecc - function to enable (reset) hardware ecc generator
- *
- *  eccmod - mode of ecc: see constants
- *
- *  eccsize - databytes used per ecc-calculation
- *
- *  chip_delay - chip dependent delay for transfering data from array to read regs (tR)
- *
- *  chip_lock - spinlock used to protect access to this structure
- *
- *  wq - wait queue to sleep on if a NAND operation is in progress
- *
- *  state - give the current state of the NAND device
- *
- *  page_shift - number of address bits in a page (column address bits)
- *
- *  data_buf - data buffer passed to/from MTD user modules
- *
- *  data_cache - data cache for redundant page access and shadow for
- *		 ECC failure
- *
- *  cache_page - number of last valid page in page_cache 
+/**
+ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
+ * @lock:               protection lock  
+ * @active:		the mtd device which holds the controller currently
+ * @wq:			wait queue to sleep on if a NAND operation is in progress
+ *                      used instead of the per chip wait queue when a hw controller is available
  */
+struct nand_hw_control {
+	spinlock_t	 lock;
+	struct nand_chip *active;
+	wait_queue_head_t wq;
+};
+
+/**
+ * struct nand_chip - NAND Private Flash Chip Data
+ * @IO_ADDR_R:		[BOARDSPECIFIC] address to read the 8 I/O lines of the flash device 
+ * @IO_ADDR_W:		[BOARDSPECIFIC] address to write the 8 I/O lines of the flash device 
+ * @read_byte:		[REPLACEABLE] read one byte from the chip
+ * @write_byte:		[REPLACEABLE] write one byte to the chip
+ * @read_word:		[REPLACEABLE] read one word from the chip
+ * @write_word:		[REPLACEABLE] write one word to the chip
+ * @write_buf:		[REPLACEABLE] write data from the buffer to the chip
+ * @read_buf:		[REPLACEABLE] read data from the chip into the buffer
+ * @verify_buf:		[REPLACEABLE] verify buffer contents against the chip data
+ * @select_chip:	[REPLACEABLE] select chip nr
+ * @block_bad:		[REPLACEABLE] check, if the block is bad
+ * @block_markbad:	[REPLACEABLE] mark the block bad
+ * @hwcontrol:		[BOARDSPECIFIC] hardwarespecific function for accesing control-lines
+ * @dev_ready:		[BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
+ *			If set to NULL no access to ready/busy is available and the ready/busy information
+ *			is read from the chip status register
+ * @cmdfunc:		[REPLACEABLE] hardwarespecific function for writing commands to the chip
+ * @waitfunc:		[REPLACEABLE] hardwarespecific function for wait on ready
+ * @calculate_ecc: 	[REPLACEABLE] function for ecc calculation or readback from ecc hardware
+ * @correct_data:	[REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
+ * @enable_hwecc:	[BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
+ *			be provided if a hardware ECC is available
+ * @erase_cmd:		[INTERN] erase command write function, selectable due to AND support
+ * @scan_bbt:		[REPLACEABLE] function to scan bad block table
+ * @eccmode:		[BOARDSPECIFIC] mode of ecc, see defines 
+ * @eccsize: 		[INTERN] databytes used per ecc-calculation
+ * @eccbytes: 		[INTERN] number of ecc bytes per ecc-calculation step
+ * @eccsteps:		[INTERN] number of ecc calculation steps per page
+ * @chip_delay:		[BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
+ * @chip_lock:		[INTERN] spinlock used to protect access to this structure and the chip
+ * @wq:			[INTERN] wait queue to sleep on if a NAND operation is in progress
+ * @state: 		[INTERN] the current state of the NAND device
+ * @page_shift:		[INTERN] number of address bits in a page (column address bits)
+ * @phys_erase_shift:	[INTERN] number of address bits in a physical eraseblock
+ * @bbt_erase_shift:	[INTERN] number of address bits in a bbt entry
+ * @chip_shift:		[INTERN] number of address bits in one chip
+ * @data_buf:		[INTERN] internal buffer for one page + oob 
+ * @oob_buf:		[INTERN] oob buffer for one eraseblock
+ * @oobdirty:		[INTERN] indicates that oob_buf must be reinitialized
+ * @data_poi:		[INTERN] pointer to a data buffer
+ * @options:		[BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
+ *			special functionality. See the defines for further explanation
+ * @badblockpos:	[INTERN] position of the bad block marker in the oob area
+ * @numchips:		[INTERN] number of physical chips
+ * @chipsize:		[INTERN] the size of one chip for multichip arrays
+ * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
+ * @pagebuf:		[INTERN] holds the pagenumber which is currently in data_buf
+ * @autooob:		[REPLACEABLE] the default (auto)placement scheme
+ * @bbt:		[INTERN] bad block table pointer
+ * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash lookup
+ * @bbt_md:		[REPLACEABLE] bad block table mirror descriptor
+ * @badblock_pattern:	[REPLACEABLE] bad block scan pattern used for initial bad block scan 
+ * @controller:		[OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
+ * @priv:		[OPTIONAL] pointer to private chip date
+ * @errstat:		[OPTIONAL] hardware specific function to perform additional error status checks 
+ *			(determine if errors are correctable)
+ */
+ 
 struct nand_chip {
-	unsigned long 	IO_ADDR_R;
-	unsigned long 	IO_ADDR_W;
+	void  __iomem	*IO_ADDR_R;
+	void  __iomem 	*IO_ADDR_W;
 	
 	u_char		(*read_byte)(struct mtd_info *mtd);
 	void		(*write_byte)(struct mtd_info *mtd, u_char byte);
@@ -224,18 +342,23 @@
 	int  		(*dev_ready)(struct mtd_info *mtd);
 	void 		(*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
 	int 		(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
-	void		(*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+	int		(*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
 	int 		(*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
 	void		(*enable_hwecc)(struct mtd_info *mtd, int mode);
+	void		(*erase_cmd)(struct mtd_info *mtd, int page);
+	int		(*scan_bbt)(struct mtd_info *mtd);
 	int		eccmode;
 	int		eccsize;
+	int		eccbytes;
 	int		eccsteps;
 	int 		chip_delay;
 	spinlock_t	chip_lock;
 	wait_queue_head_t wq;
 	nand_state_t 	state;
 	int 		page_shift;
-	int		erase_shift;
+	int		phys_erase_shift;
+	int		bbt_erase_shift;
+	int		chip_shift;
 	u_char 		*data_buf;
 	u_char		*oob_buf;
 	int		oobdirty;
@@ -247,7 +370,13 @@
 	int		pagemask;
 	int		pagebuf;
 	struct nand_oobinfo	*autooob;
+	uint8_t		*bbt;
+	struct nand_bbt_descr	*bbt_td;
+	struct nand_bbt_descr	*bbt_md;
+	struct nand_bbt_descr	*badblock_pattern;
+	struct nand_hw_control  *controller;
 	void		*priv;
+	int		(*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
 };
 
 /*
@@ -257,21 +386,22 @@
 #define NAND_MFR_SAMSUNG	0xec
 #define NAND_MFR_FUJITSU	0x04
 #define NAND_MFR_NATIONAL	0x8f
-
-/*
- * NAND Flash Device ID Structure
- *
- * Structure overview:
- *
- *  name  	Identify the device type
- *  id   	device ID code
- *  pagesize   	Pagesize in bytes. Either 256 or 512 or 0
+#define NAND_MFR_RENESAS	0x07
+#define NAND_MFR_STMICRO	0x20
+#define NAND_MFR_HYNIX          0xad
+
+/**
+ * struct nand_flash_dev - NAND Flash Device ID Structure
+ *
+ * @name:  	Identify the device type
+ * @id:   	device ID code
+ * @pagesize:  	Pagesize in bytes. Either 256 or 512 or 0
  *		If the pagesize is 0, then the real pagesize 
  *		and the eraseize are determined from the
  *		extended id bytes in the chip
- *  erasesize 	Size of an erase block in the flash device.
- *  chipsize  	Total chipsize in Mega Bytes
- *  options	Bitfield to store chip relevant options
+ * @erasesize: 	Size of an erase block in the flash device.
+ * @chipsize:  	Total chipsize in Mega Bytes
+ * @options:	Bitfield to store chip relevant options
  */
 struct nand_flash_dev {
 	char *name;
@@ -282,10 +412,10 @@
 	unsigned long options;
 };
 
-/*
- * NAND Flash Manufacturer ID Structure
- *  name - Manufacturer name
- *  id - manufacturer ID code of device.
+/**
+ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
+ * @name:	Manufacturer name
+ * @id: 	manufacturer ID code of device.
 */
 struct nand_manufacturers {
 	int id;
@@ -295,6 +425,84 @@
 extern struct nand_flash_dev nand_flash_ids[];
 extern struct nand_manufacturers nand_manuf_ids[];
 
+/** 
+ * struct nand_bbt_descr - bad block table descriptor
+ * @options:	options for this descriptor
+ * @pages:	the page(s) where we find the bbt, used with option BBT_ABSPAGE
+ *		when bbt is searched, then we store the found bbts pages here.
+ *		Its an array and supports up to 8 chips now
+ * @offs:	offset of the pattern in the oob area of the page
+ * @veroffs:	offset of the bbt version counter in the oob are of the page
+ * @version:	version read from the bbt page during scan
+ * @len:	length of the pattern, if 0 no pattern check is performed
+ * @maxblocks:	maximum number of blocks to search for a bbt. This number of
+ *		blocks is reserved at the end of the device where the tables are 
+ *		written.
+ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
+ *              bad) block in the stored bbt
+ * @pattern:	pattern to identify bad block table or factory marked good / 
+ *		bad blocks, can be NULL, if len = 0
+ *
+ * Descriptor for the bad block table marker and the descriptor for the 
+ * pattern which identifies good and bad blocks. The assumption is made
+ * that the pattern and the version count are always located in the oob area
+ * of the first block.
+ */
+struct nand_bbt_descr {
+	int	options;
+	int	pages[NAND_MAX_CHIPS];
+	int	offs;
+	int	veroffs;
+	uint8_t	version[NAND_MAX_CHIPS];
+	int	len;
+	int 	maxblocks;
+	int	reserved_block_code;
+	uint8_t	*pattern;
+};
+
+/* Options for the bad block table descriptors */
+
+/* The number of bits used per block in the bbt on the device */
+#define NAND_BBT_NRBITS_MSK	0x0000000F
+#define NAND_BBT_1BIT		0x00000001
+#define NAND_BBT_2BIT		0x00000002
+#define NAND_BBT_4BIT		0x00000004
+#define NAND_BBT_8BIT		0x00000008
+/* The bad block table is in the last good block of the device */
+#define	NAND_BBT_LASTBLOCK	0x00000010
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_ABSPAGE	0x00000020
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_SEARCH		0x00000040
+/* bbt is stored per chip on multichip devices */
+#define NAND_BBT_PERCHIP	0x00000080
+/* bbt has a version counter at offset veroffs */
+#define NAND_BBT_VERSION	0x00000100
+/* Create a bbt if none axists */
+#define NAND_BBT_CREATE		0x00000200
+/* Search good / bad pattern through all pages of a block */
+#define NAND_BBT_SCANALLPAGES	0x00000400
+/* Scan block empty during good / bad block scan */
+#define NAND_BBT_SCANEMPTY	0x00000800
+/* Write bbt if neccecary */
+#define NAND_BBT_WRITE		0x00001000
+/* Read and write back block contents when writing bbt */
+#define NAND_BBT_SAVECONTENT	0x00002000
+/* Search good / bad pattern on the first and the second page */
+#define NAND_BBT_SCAN2NDPAGE	0x00004000
+
+/* The maximum number of blocks to scan for a bbt */
+#define NAND_BBT_SCAN_MAXBLOCKS	4
+
+extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
+extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
+extern int nand_default_bbt (struct mtd_info *mtd);
+extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
+extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
+extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+                             size_t * retlen, u_char * buf, u_char * oob_buf,
+                             struct nand_oobinfo *oobsel, int flags);
+
 /*
 * Constants for oob configuration
 */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/nand_ecc.h linux-2.4.30/include/linux/mtd/nand_ecc.h
--- linux-2.4.30-orig/include/linux/mtd/nand_ecc.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/nand_ecc.h	2006-12-11 18:18:37.000000000 +0100
@@ -3,7 +3,7 @@
  *
  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *
- * $Id: nand_ecc.h,v 1.3 2003/07/01 23:31:15 dwmw2 Exp $
+ * $Id: nand_ecc.h,v 1.3 2005/10/12 14:52:47 lel Exp $
  *
  * 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
@@ -20,7 +20,7 @@
 /*
  * Calculate 3 byte ECC code for 256 byte block
  */
-void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
 
 /*
  * Detect and correct a 1 bit error for 256 byte block
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/nand_ids.h linux-2.4.30/include/linux/mtd/nand_ids.h
--- linux-2.4.30-orig/include/linux/mtd/nand_ids.h	2005-05-12 17:57:53.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/nand_ids.h	2006-12-11 18:18:37.000000000 +0100
@@ -4,7 +4,7 @@
  *  Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
  *                     Steven J. Hill <sjhill@cotw.com>
  *
- * $Id: nand_ids.h,v 1.1 2000/10/13 16:16:26 mdeans Exp $
+ * $Id: nand_ids.h,v 1.2 2005/09/28 14:19:52 lel Exp $
  *
  * 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
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/nftl.h linux-2.4.30/include/linux/mtd/nftl.h
--- linux-2.4.30-orig/include/linux/mtd/nftl.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/nftl.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: nftl.h,v 1.15 2004/05/05 15:25:23 dwmw2 Exp $
+ * $Id: nftl.h,v 1.3 2005/10/12 14:52:47 lel Exp $
  *
  * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
  */
@@ -37,6 +37,7 @@
         unsigned int nb_blocks;		/* number of physical blocks */
         unsigned int nb_boot_blocks;	/* number of blocks used by the bios */
         struct erase_info instr;
+	struct nand_oobinfo oobinfo;
 };
 
 int NFTL_mount(struct NFTLrecord *s);
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/onenand.h linux-2.4.30/include/linux/mtd/onenand.h
--- linux-2.4.30-orig/include/linux/mtd/onenand.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/include/linux/mtd/onenand.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,155 @@
+/*
+ *  linux/include/linux/mtd/onenand.h
+ *
+ *  Copyright (C) 2005 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.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.
+ */
+
+#ifndef __LINUX_MTD_ONENAND_H
+#define __LINUX_MTD_ONENAND_H
+
+#include <linux/spinlock.h>
+#include <linux/mtd/onenand_regs.h>
+#include <linux/mtd/bbm.h>
+
+#define MAX_BUFFERRAM		2
+#define MAX_ONENAND_PAGESIZE	(2048 + 64)
+
+/* Scan and identify a OneNAND device */
+extern int onenand_scan(struct mtd_info *mtd, int max_chips);
+/* Free resources held by the OneNAND device */
+extern void onenand_release(struct mtd_info *mtd);
+
+/**
+ * onenand_state_t - chip states
+ * Enumeration for OneNAND flash chip state
+ */
+typedef enum {
+	FL_READY,
+	FL_READING,
+	FL_WRITING,
+	FL_ERASING,
+	FL_SYNCING,
+	FL_UNLOCKING,
+	FL_LOCKING,
+	FL_PM_SUSPENDED,
+} onenand_state_t;
+
+/**
+ * struct onenand_bufferram - OneNAND BufferRAM Data
+ * @param block		block address in BufferRAM
+ * @param page		page address in BufferRAM
+ * @param valid		valid flag
+ */
+struct onenand_bufferram {
+	int block;
+	int page;
+	int valid;
+};
+
+/**
+ * struct onenand_chip - OneNAND Private Flash Chip Data
+ * @param base		[BOARDSPECIFIC] address to access OneNAND
+ * @param chipsize	[INTERN] the size of one chip for multichip arrays
+ * @param device_id	[INTERN] device ID
+ * @param verstion_id	[INTERN] version ID
+ * @param options	[BOARDSPECIFIC] various chip options. They can partly be set to inform onenand_scan about
+ * @param erase_shift	[INTERN] number of address bits in a block
+ * @param page_shift	[INTERN] number of address bits in a page
+ * @param ppb_shift	[INTERN] number of address bits in a pages per block
+ * @param page_mask	[INTERN] a page per block mask
+ * @param bufferam_index	[INTERN] BufferRAM index
+ * @param bufferam	[INTERN] BufferRAM info
+ * @param readw		[REPLACEABLE] hardware specific function for read short
+ * @param writew	[REPLACEABLE] hardware specific function for write short
+ * @param command	[REPLACEABLE] hardware specific function for writing commands to the chip
+ * @param wait		[REPLACEABLE] hardware specific function for wait on ready
+ * @param read_bufferram	[REPLACEABLE] hardware specific function for BufferRAM Area
+ * @param write_bufferram	[REPLACEABLE] hardware specific function for BufferRAM Area
+ * @param read_word	[REPLACEABLE] hardware specific function for read register of OneNAND
+ * @param write_word	[REPLACEABLE] hardware specific function for write register of OneNAND
+ * @param scan_bbt	[REPLACEALBE] hardware specific function for scaning Bad block Table
+ * @param chip_lock	[INTERN] spinlock used to protect access to this structure and the chip
+ * @param wq		[INTERN] wait queue to sleep on if a OneNAND operation is in progress
+ * @param state		[INTERN] the current state of the OneNAND device
+ * @param autooob	[REPLACEABLE] the default (auto)placement scheme
+ * @param bbm		[REPLACEABLE] pointer to Bad Block Management 
+ * @param priv		[OPTIONAL] pointer to private chip date
+ */
+struct onenand_chip {
+	void __iomem		*base;
+	unsigned int		chipsize;
+	unsigned int		device_id;
+	unsigned int		density_mask;
+	unsigned int		options;
+
+	unsigned int		erase_shift;
+	unsigned int		page_shift;
+	unsigned int		ppb_shift;	/* Pages per block shift */
+	unsigned int		page_mask;
+
+	unsigned int		bufferram_index;
+	struct onenand_bufferram	bufferram[MAX_BUFFERRAM];
+
+	int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len);
+	int (*wait)(struct mtd_info *mtd, int state);
+	int (*read_bufferram)(struct mtd_info *mtd, int area,
+			unsigned char *buffer, int offset, size_t count);
+	int (*write_bufferram)(struct mtd_info *mtd, int area,
+			const unsigned char *buffer, int offset, size_t count);
+	unsigned short (*read_word)(void __iomem *addr);
+	void (*write_word)(unsigned short value, void __iomem *addr);
+	void (*mmcontrol)(struct mtd_info *mtd, int sync_read);
+	int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
+	int (*scan_bbt)(struct mtd_info *mtd);
+
+	spinlock_t		chip_lock;
+	wait_queue_head_t	wq;
+	onenand_state_t		state;
+
+	struct nand_oobinfo	*autooob;
+
+	void 			*bbm;
+
+	void			*priv;
+};
+
+/*
+ * Helper macros
+ */
+#define ONENAND_CURRENT_BUFFERRAM(this)		(this->bufferram_index)
+#define ONENAND_NEXT_BUFFERRAM(this)		(this->bufferram_index ^ 1)
+#define ONENAND_SET_NEXT_BUFFERRAM(this)	(this->bufferram_index ^= 1)
+
+#define ONENAND_GET_SYS_CFG1(this)					\
+	(this->read_word(this->base + ONENAND_REG_SYS_CFG1))
+#define ONENAND_SET_SYS_CFG1(v, this)					\
+	(this->write_word(v, this->base + ONENAND_REG_SYS_CFG1))
+
+/*
+ * Options bits
+ */
+#define ONENAND_CONT_LOCK		(0x0001)
+
+
+/*
+ * OneNAND Flash Manufacturer ID Codes
+ */
+#define ONENAND_MFR_SAMSUNG	0xec
+#define ONENAND_MFR_UNKNOWN	0x00
+
+/**
+ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
+ * @param name:		Manufacturer name
+ * @param id:		manufacturer ID code of device.
+*/
+struct onenand_manufacturers {
+        int id;
+        char *name;
+};
+
+#endif	/* __LINUX_MTD_ONENAND_H */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/onenand_regs.h linux-2.4.30/include/linux/mtd/onenand_regs.h
--- linux-2.4.30-orig/include/linux/mtd/onenand_regs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/include/linux/mtd/onenand_regs.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,180 @@
+/*
+ *  linux/include/linux/mtd/onenand_regs.h
+ *
+ *  OneNAND Register header file
+ *
+ *  Copyright (C) 2005 Samsung Electronics
+ *
+ * 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.
+ */
+
+#ifndef __ONENAND_REG_H
+#define __ONENAND_REG_H
+
+/* Memory Address Map Translation (Word order) */
+#define ONENAND_MEMORY_MAP(x)		((x) << 1)
+
+/*
+ * External BufferRAM area
+ */
+#define	ONENAND_BOOTRAM			ONENAND_MEMORY_MAP(0x0000)
+#define	ONENAND_DATARAM			ONENAND_MEMORY_MAP(0x0200)
+#define	ONENAND_SPARERAM		ONENAND_MEMORY_MAP(0x8010)
+
+/*
+ * OneNAND Registers
+ */
+#define ONENAND_REG_MANUFACTURER_ID	ONENAND_MEMORY_MAP(0xF000)
+#define ONENAND_REG_DEVICE_ID		ONENAND_MEMORY_MAP(0xF001)
+#define ONENAND_REG_VERSION_ID		ONENAND_MEMORY_MAP(0xF002)
+#define ONENAND_REG_DATA_BUFFER_SIZE	ONENAND_MEMORY_MAP(0xF003)
+#define ONENAND_REG_BOOT_BUFFER_SIZE	ONENAND_MEMORY_MAP(0xF004)
+#define ONENAND_REG_NUM_BUFFERS		ONENAND_MEMORY_MAP(0xF005)
+#define ONENAND_REG_TECHNOLOGY		ONENAND_MEMORY_MAP(0xF006)
+
+#define ONENAND_REG_START_ADDRESS1	ONENAND_MEMORY_MAP(0xF100)
+#define ONENAND_REG_START_ADDRESS2	ONENAND_MEMORY_MAP(0xF101)
+#define ONENAND_REG_START_ADDRESS3	ONENAND_MEMORY_MAP(0xF102)
+#define ONENAND_REG_START_ADDRESS4	ONENAND_MEMORY_MAP(0xF103)
+#define ONENAND_REG_START_ADDRESS5	ONENAND_MEMORY_MAP(0xF104)
+#define ONENAND_REG_START_ADDRESS6	ONENAND_MEMORY_MAP(0xF105)
+#define ONENAND_REG_START_ADDRESS7	ONENAND_MEMORY_MAP(0xF106)
+#define ONENAND_REG_START_ADDRESS8	ONENAND_MEMORY_MAP(0xF107)
+
+#define ONENAND_REG_START_BUFFER	ONENAND_MEMORY_MAP(0xF200)
+#define ONENAND_REG_COMMAND		ONENAND_MEMORY_MAP(0xF220)
+#define ONENAND_REG_SYS_CFG1		ONENAND_MEMORY_MAP(0xF221)
+#define ONENAND_REG_SYS_CFG2		ONENAND_MEMORY_MAP(0xF222)
+#define ONENAND_REG_CTRL_STATUS		ONENAND_MEMORY_MAP(0xF240)
+#define ONENAND_REG_INTERRUPT		ONENAND_MEMORY_MAP(0xF241)
+#define ONENAND_REG_START_BLOCK_ADDRESS	ONENAND_MEMORY_MAP(0xF24C)
+#define ONENAND_REG_END_BLOCK_ADDRESS	ONENAND_MEMORY_MAP(0xF24D)
+#define ONENAND_REG_WP_STATUS		ONENAND_MEMORY_MAP(0xF24E)
+
+#define ONENAND_REG_ECC_STATUS		ONENAND_MEMORY_MAP(0xFF00)
+#define ONENAND_REG_ECC_M0		ONENAND_MEMORY_MAP(0xFF01)
+#define ONENAND_REG_ECC_S0		ONENAND_MEMORY_MAP(0xFF02)
+#define ONENAND_REG_ECC_M1		ONENAND_MEMORY_MAP(0xFF03)
+#define ONENAND_REG_ECC_S1		ONENAND_MEMORY_MAP(0xFF04)
+#define ONENAND_REG_ECC_M2		ONENAND_MEMORY_MAP(0xFF05)
+#define ONENAND_REG_ECC_S2		ONENAND_MEMORY_MAP(0xFF06)
+#define ONENAND_REG_ECC_M3		ONENAND_MEMORY_MAP(0xFF07)
+#define ONENAND_REG_ECC_S3		ONENAND_MEMORY_MAP(0xFF08)
+
+/*
+ * Device ID Register F001h (R)
+ */
+#define ONENAND_DEVICE_DENSITY_SHIFT	(4)
+#define ONENAND_DEVICE_IS_DDP		(1 << 3)
+#define ONENAND_DEVICE_IS_DEMUX		(1 << 2)
+#define ONENAND_DEVICE_VCC_MASK		(0x3)
+
+#define ONENAND_DEVICE_DENSITY_512Mb	(0x002)
+
+/*
+ * Version ID Register F002h (R)
+ */
+#define ONENAND_VERSION_PROCESS_SHIFT	(8)
+
+/*
+ * Start Address 1 F100h (R/W)
+ */
+#define ONENAND_DDP_SHIFT		(15)
+
+/*
+ * Start Address 8 F107h (R/W)
+ */
+#define ONENAND_FPA_MASK		(0x3f)
+#define ONENAND_FPA_SHIFT		(2)
+#define ONENAND_FSA_MASK		(0x03)
+
+/*
+ * Start Buffer Register F200h (R/W)
+ */
+#define ONENAND_BSA_MASK		(0x03)
+#define ONENAND_BSA_SHIFT		(8)
+#define ONENAND_BSA_BOOTRAM		(0 << 2)
+#define ONENAND_BSA_DATARAM0		(2 << 2)
+#define ONENAND_BSA_DATARAM1		(3 << 2)
+#define ONENAND_BSC_MASK		(0x03)
+
+/*
+ * Command Register F220h (R/W)
+ */
+#define ONENAND_CMD_READ		(0x00)
+#define ONENAND_CMD_READOOB		(0x13)
+#define ONENAND_CMD_PROG		(0x80)
+#define ONENAND_CMD_PROGOOB		(0x1A)
+#define ONENAND_CMD_UNLOCK		(0x23)
+#define ONENAND_CMD_LOCK		(0x2A)
+#define ONENAND_CMD_LOCK_TIGHT		(0x2C)
+#define ONENAND_CMD_ERASE		(0x94)
+#define ONENAND_CMD_RESET		(0xF0)
+#define ONENAND_CMD_READID		(0x90)
+
+/* NOTE: Those are not *REAL* commands */
+#define ONENAND_CMD_BUFFERRAM		(0x1978)
+
+/*
+ * System Configuration 1 Register F221h (R, R/W)
+ */
+#define ONENAND_SYS_CFG1_SYNC_READ	(1 << 15)
+#define ONENAND_SYS_CFG1_BRL_7		(7 << 12)
+#define ONENAND_SYS_CFG1_BRL_6		(6 << 12)
+#define ONENAND_SYS_CFG1_BRL_5		(5 << 12)
+#define ONENAND_SYS_CFG1_BRL_4		(4 << 12)
+#define ONENAND_SYS_CFG1_BRL_3		(3 << 12)
+#define ONENAND_SYS_CFG1_BRL_10		(2 << 12)
+#define ONENAND_SYS_CFG1_BRL_9		(1 << 12)
+#define ONENAND_SYS_CFG1_BRL_8		(0 << 12)
+#define ONENAND_SYS_CFG1_BRL_SHIFT	(12)
+#define ONENAND_SYS_CFG1_BL_32		(4 << 9)
+#define ONENAND_SYS_CFG1_BL_16		(3 << 9)
+#define ONENAND_SYS_CFG1_BL_8		(2 << 9)
+#define ONENAND_SYS_CFG1_BL_4		(1 << 9)
+#define ONENAND_SYS_CFG1_BL_CONT	(0 << 9)
+#define ONENAND_SYS_CFG1_BL_SHIFT	(9)
+#define ONENAND_SYS_CFG1_NO_ECC		(1 << 8)
+#define ONENAND_SYS_CFG1_RDY		(1 << 7)
+#define ONENAND_SYS_CFG1_INT		(1 << 6)
+#define ONENAND_SYS_CFG1_IOBE		(1 << 5)
+#define ONENAND_SYS_CFG1_RDY_CONF	(1 << 4)
+
+/*
+ * Controller Status Register F240h (R)
+ */
+#define ONENAND_CTRL_ONGO		(1 << 15)
+#define ONENAND_CTRL_LOCK		(1 << 14)
+#define ONENAND_CTRL_LOAD		(1 << 13)
+#define ONENAND_CTRL_PROGRAM		(1 << 12)
+#define ONENAND_CTRL_ERASE		(1 << 11)
+#define ONENAND_CTRL_ERROR		(1 << 10)
+#define ONENAND_CTRL_RSTB		(1 << 7)
+
+/*
+ * Interrupt Status Register F241h (R)
+ */
+#define ONENAND_INT_MASTER		(1 << 15)
+#define ONENAND_INT_READ		(1 << 7)
+#define ONENAND_INT_WRITE		(1 << 6)
+#define ONENAND_INT_ERASE		(1 << 5)
+#define ONENAND_INT_RESET		(1 << 4)
+#define ONENAND_INT_CLEAR		(0 << 0)
+
+/*
+ * NAND Flash Write Protection Status Register F24Eh (R)
+ */
+#define ONENAND_WP_US			(1 << 2)
+#define ONENAND_WP_LS			(1 << 1)
+#define ONENAND_WP_LTS			(1 << 0)
+
+/*
+ * ECC Status Reigser FF00h (R)
+ */
+#define ONENAND_ECC_1BIT		(1 << 0)
+#define ONENAND_ECC_2BIT		(1 << 1)
+#define ONENAND_ECC_2BIT_ALL		(0xAAAA)
+
+#endif	/* __ONENAND_REG_H */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/partitions.h linux-2.4.30/include/linux/mtd/partitions.h
--- linux-2.4.30-orig/include/linux/mtd/partitions.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/partitions.h	2006-12-11 18:18:37.000000000 +0100
@@ -5,7 +5,7 @@
  *
  * This code is GPL
  *
- * $Id: partitions.h,v 1.15 2003/07/09 11:15:43 dwmw2 Exp $
+ * $Id: partitions.h,v 1.3 2005/10/12 14:52:47 lel Exp $
  */
 
 #ifndef MTD_PARTITIONS_H
@@ -64,7 +64,6 @@
 	int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long);
 };
 
-extern struct mtd_part_parser *get_partition_parser(const char *name);
 extern int register_mtd_parser(struct mtd_part_parser *parser);
 extern int deregister_mtd_parser(struct mtd_part_parser *parser);
 extern int parse_mtd_partitions(struct mtd_info *master, const char **types, 
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/physmap.h linux-2.4.30/include/linux/mtd/physmap.h
--- linux-2.4.30-orig/include/linux/mtd/physmap.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/physmap.h	2006-12-11 18:18:37.000000000 +0100
@@ -2,6 +2,8 @@
  * For boards with physically mapped flash and using 
  * drivers/mtd/maps/physmap.c mapping driver.
  *
+ * $Id: physmap.h,v 1.3 2005/10/12 14:52:48 lel Exp $
+ *
  * Copyright (C) 2003 MontaVista Software Inc.
  * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
  *
@@ -31,11 +33,11 @@
 /*
  * Board needs to specify the exact mapping during their setup time.
  */
-static inline void physmap_configure(unsigned long addr, unsigned long size, int buswidth, void (*set_vpp)(struct map_info *, int) )
+static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) )
 {
 	physmap_map.phys = addr;
 	physmap_map.size = size;
-	physmap_map.buswidth = buswidth;
+	physmap_map.bankwidth = bankwidth;
 	physmap_map.set_vpp = set_vpp;
 }
 
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/plat-ram.h linux-2.4.30/include/linux/mtd/plat-ram.h
--- linux-2.4.30-orig/include/linux/mtd/plat-ram.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/include/linux/mtd/plat-ram.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,35 @@
+/* linux/include/mtd/plat-ram.h
+ *
+ * (c) 2004 Simtec Electronics
+ *	http://www.simtec.co.uk/products/SWLINUX/
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * Generic platform device based RAM map
+ *
+ * $Id: plat-ram.h,v 1.3 2005/10/12 14:52:48 lel Exp $
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_MTD_PLATRAM_H
+#define __LINUX_MTD_PLATRAM_H __FILE__
+
+#define PLATRAM_RO (0)
+#define PLATRAM_RW (1)
+
+struct platdata_mtd_ram {
+	char			*mapname;
+	char		       **probes;
+	struct mtd_partition	*partitions;
+	int			 nr_partitions;
+	int			 bankwidth;
+
+	/* control callbacks */
+
+	void	(*set_rw)(struct device *dev, int to);
+};
+
+#endif /* __LINUX_MTD_PLATRAM_H */
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/pmc551.h linux-2.4.30/include/linux/mtd/pmc551.h
--- linux-2.4.30-orig/include/linux/mtd/pmc551.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/linux/mtd/pmc551.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: pmc551.h,v 1.5 2003/01/24 16:49:53 dwmw2 Exp $
+ * $Id: pmc551.h,v 1.3 2005/10/12 14:52:48 lel Exp $
  *
  * PMC551 PCI Mezzanine Ram Device
  *
@@ -17,7 +17,7 @@
 
 #include <linux/mtd/mtd.h>
 
-#define PMC551_VERSION "$Id: pmc551.h,v 1.5 2003/01/24 16:49:53 dwmw2 Exp $\n"\
+#define PMC551_VERSION "$Id: pmc551.h,v 1.3 2005/10/12 14:52:48 lel Exp $\n"\
        "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n"
 
 /*
diff -x asm -Naur linux-2.4.30-orig/include/linux/mtd/xip.h linux-2.4.30/include/linux/mtd/xip.h
--- linux-2.4.30-orig/include/linux/mtd/xip.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30/include/linux/mtd/xip.h	2006-12-11 18:18:37.000000000 +0100
@@ -0,0 +1,102 @@
+/*
+ * MTD primitives for XIP support
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Nov 2, 2004
+ * Copyright:	(C) 2004 MontaVista Software, Inc.
+ *
+ * This XIP support for MTD has been loosely inspired
+ * by an earlier patch authored by David Woodhouse.
+ *
+ * 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.
+ *
+ * $Id: xip.h,v 1.3 2005/10/12 14:52:48 lel Exp $
+ */
+
+#ifndef __LINUX_MTD_XIP_H__
+#define __LINUX_MTD_XIP_H__
+
+#include <linux/config.h>
+
+#ifdef CONFIG_MTD_XIP
+
+/*
+ * Function that are modifying the flash state away from array mode must
+ * obviously not be running from flash.  The __xipram is therefore marking
+ * those functions so they get relocated to ram.
+ */
+#define __xipram __attribute__ ((__section__ (".data")))
+
+/*
+ * We really don't want gcc to guess anything.
+ * We absolutely _need_ proper inlining.
+ */
+#include <linux/compiler.h>
+
+/*
+ * Each architecture has to provide the following macros.  They must access
+ * the hardware directly and not rely on any other (XIP) functions since they
+ * won't be available when used (flash not in array mode).
+ *
+ * xip_irqpending()
+ *
+ * 	return non zero when any hardware interrupt is pending.
+ *
+ * xip_currtime()
+ *
+ * 	return a platform specific time reference to be used with
+ * 	xip_elapsed_since().
+ *
+ * xip_elapsed_since(x)
+ *
+ * 	return in usecs the elapsed timebetween now and the reference x as
+ * 	returned by xip_currtime().
+ *
+ * 	note 1: convertion to usec can be approximated, as long as the
+ * 		returned value is <= the real elapsed time.
+ * 	note 2: this should be able to cope with a few seconds without
+ * 		overflowing.
+ *
+ * xip_iprefetch()
+ *  
+ *      Macro to fill instruction prefetch
+ *	e.g. a series of nops:  asm volatile (".rep 8; nop; .endr"); 
+ */
+
+#include <asm/mtd-xip.h>
+
+#ifndef xip_irqpending
+
+#warning "missing IRQ and timer primitives for XIP MTD support"
+#warning "some of the XIP MTD support code will be disabled"
+#warning "your system will therefore be unresponsive when writing or erasing flash"
+
+#define xip_irqpending()	(0)
+#define xip_currtime()		(0)
+#define xip_elapsed_since(x)	(0)
+
+#endif
+
+#ifndef xip_iprefetch
+#define xip_iprefetch()		do { } while (0)
+#endif
+
+/*
+ * xip_cpu_idle() is used when waiting for a delay equal or larger than
+ * the system timer tick period.  This should put the CPU into idle mode
+ * to save power and to be woken up only when some interrupts are pending.
+ * This should not rely upon standard kernel code.
+ */
+#ifndef xip_cpu_idle
+#define xip_cpu_idle()  do { } while (0)
+#endif
+
+#else
+
+#define __xipram
+
+#endif /* CONFIG_MTD_XIP */
+
+#endif /* __LINUX_MTD_XIP_H__ */
diff -x asm -Naur linux-2.4.30-orig/include/linux/soundcard.h linux-2.4.30/include/linux/soundcard.h
--- linux-2.4.30-orig/include/linux/soundcard.h	2005-05-12 17:57:55.000000000 +0200
+++ linux-2.4.30/include/linux/soundcard.h	2006-12-11 18:18:37.000000000 +0100
@@ -564,6 +564,8 @@
 #	define AFMT_U32_LE	        0x00020000	
 #	define AFMT_U32_BE	        0x00040000	
 
+#define SNDCTL_DSP_RXTX_LOOP		_SIO  ('P', 12)	// i3 patch
+
 /*
  * Buffer status queries.
  */
diff -x asm -Naur linux-2.4.30-orig/include/mtd/mtd-abi.h linux-2.4.30/include/mtd/mtd-abi.h
--- linux-2.4.30-orig/include/mtd/mtd-abi.h	2005-05-12 18:01:29.000000000 +0200
+++ linux-2.4.30/include/mtd/mtd-abi.h	2006-12-11 18:18:37.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * $Id: mtd-abi.h,v 1.1 2004/05/05 14:44:57 dwmw2 Exp $
+ * $Id: mtd-abi.h,v 1.3 2005/10/12 14:52:26 lel Exp $
  *
  * Portions of MTD ABI definition which are shared by kernel and user space 
  */
@@ -7,6 +7,11 @@
 #ifndef __MTD_ABI_H__
 #define __MTD_ABI_H__
 
+#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into
+		    separate files was to avoid #ifdef __KERNEL__ */
+#define __user
+#endif
+
 struct erase_info_user {
 	uint32_t start;
 	uint32_t length;
@@ -15,7 +20,7 @@
 struct mtd_oob_buf {
 	uint32_t start;
 	uint32_t length;
-	unsigned char *ptr;
+	unsigned char __user *ptr;
 };
 
 #define MTD_ABSENT		0
@@ -24,6 +29,7 @@
 #define MTD_NORFLASH		3
 #define MTD_NANDFLASH		4
 #define MTD_PEROM		5
+#define MTD_DATAFLASH		6
 #define MTD_OTHER		14
 #define MTD_UNKNOWN		15
 
@@ -35,6 +41,8 @@
 #define MTD_XIP			32	// eXecute-In-Place possible
 #define MTD_OOB			64	// Out-of-band data (NAND flash)
 #define MTD_ECC			128	// Device capable of automatic ECC
+#define MTD_NO_VIRTBLOCKS	256	// Virtual blocks not allowed
+#define MTD_PROGRAM_REGIONS	512	// Configurable Programming Regions
 
 // Some common devices / combinations of capabilities
 #define MTD_CAP_ROM		0
@@ -51,8 +59,15 @@
 
 /* ECC byte placement */
 #define MTD_NANDECC_OFF		0	// Switch off ECC (Not recommended)
-#define MTD_NANDECC_PLACE	1	// Use the given placement in the structure
+#define MTD_NANDECC_PLACE	1	// Use the given placement in the structure (YAFFS1 legacy mode)
 #define MTD_NANDECC_AUTOPLACE	2	// Use the default placement scheme
+#define MTD_NANDECC_PLACEONLY	3	// Use the given placement in the structure (Do not store ecc result on read)
+#define MTD_NANDECC_AUTOPL_USR 	4	// Use the given autoplacement scheme rather than using the default
+
+/* OTP mode selection */
+#define MTD_OTP_OFF		0
+#define MTD_OTP_FACTORY		1
+#define MTD_OTP_USER		2
 
 struct mtd_info_user {
 	uint8_t type;
@@ -73,6 +88,12 @@
 	uint32_t regionindex;
 };
 
+struct otp_info {
+	uint32_t start;
+	uint32_t length;
+	uint32_t locked;
+};
+
 #define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
 #define MEMERASE                _IOW('M', 2, struct erase_info_user)
 #define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
@@ -82,12 +103,19 @@
 #define MEMGETREGIONCOUNT	_IOR('M', 7, int)
 #define MEMGETREGIONINFO	_IOWR('M', 8, struct region_info_user)
 #define MEMSETOOBSEL		_IOW('M', 9, struct nand_oobinfo)
+#define MEMGETOOBSEL		_IOR('M', 10, struct nand_oobinfo)
+#define MEMGETBADBLOCK		_IOW('M', 11, loff_t)
+#define MEMSETBADBLOCK		_IOW('M', 12, loff_t)
+#define OTPSELECT		_IOR('M', 13, int)
+#define OTPGETREGIONCOUNT	_IOW('M', 14, int)
+#define OTPGETREGIONINFO	_IOW('M', 15, struct otp_info)
+#define OTPLOCK		_IOR('M', 16, struct otp_info)
 
 struct nand_oobinfo {
-	int	useecc;
-	int	eccbytes;
-	int	eccpos[24];
-	int	oobfree[8][2];
+	uint32_t useecc;
+	uint32_t eccbytes;
+	uint32_t oobfree[8][2];
+	uint32_t eccpos[32];
 };
 
 #endif /* __MTD_ABI_H__ */
diff -x asm -Naur linux-2.4.30-orig/lib/inflate.c linux-2.4.30/lib/inflate.c
--- linux-2.4.30-orig/lib/inflate.c	2005-05-12 17:57:56.000000000 +0200
+++ linux-2.4.30/lib/inflate.c	2006-12-11 18:18:37.000000000 +0100
@@ -961,27 +961,45 @@
   int r;                /* result code */
   unsigned h;           /* maximum struct huft's malloc'ed */
   void *ptr;
+  volatile unsigned int * gpio2 = (unsigned int *)0xB1700008L;
+  int ledcounter = 0;
+  int ledstate = 0;
 
   /* initialize window, bit buffer */
   wp = 0;
   bk = 0;
   bb = 0;
 
+//  printk("inflate()\n");
 
   /* decompress until the last block */
   h = 0;
   do {
+      if((ledcounter++ % 15) == 0) {
+	  if(ledstate)
+	      *gpio2 = 0x10180000;
+	  else
+	      *gpio2 = 0x10181018;
+	  ledstate = !ledstate;
+      }
+	  
     hufts = 0;
+//    printk("gzip_mark()\n");
     gzip_mark(&ptr);
+//    printk("inflate_block()\n");
     if ((r = inflate_block(&e)) != 0) {
       gzip_release(&ptr);	    
+      *gpio2 = 0x10181018;
       return r;
     }
+//    printk("done - ledcounter=%d, jiffies=%d\n", ledcounter, jiffies);
     gzip_release(&ptr);
     if (hufts > h)
       h = hufts;
   } while (!e);
 
+  *gpio2 = 0x10181018;
+
   /* Undo too much lookahead. The next read will be byte aligned so we
    * can discard unused bits in the last meaningful byte.
    */
diff -x asm -Naur linux-2.4.30-orig/lib/rbtree.c linux-2.4.30/lib/rbtree.c
--- linux-2.4.30-orig/lib/rbtree.c	2005-05-12 17:57:56.000000000 +0200
+++ linux-2.4.30/lib/rbtree.c	2006-12-11 18:18:37.000000000 +0100
@@ -365,3 +365,27 @@
 	return node->rb_parent;
 }
 EXPORT_SYMBOL(rb_prev);
+
+/* Included by I3 - copied from fs/jffs2 */
+void rb_replace_node(rb_node_t *victim, rb_node_t *new, rb_root_t *root)
+{
+        rb_node_t *parent = victim->rb_parent;
+
+        /* Set the surrounding nodes to point to the replacement */
+        if (parent) {
+                if (victim == parent->rb_left)
+                        parent->rb_left = new;
+                else
+                        parent->rb_right = new;
+        } else {
+                root->rb_node = new;
+        }
+        if (victim->rb_left)
+                victim->rb_left->rb_parent = new;
+        if (victim->rb_right)
+                victim->rb_right->rb_parent = new;
+
+        /* Copy the pointers/colour from the victim to the replacement */
+        *new = *victim;
+}
+EXPORT_SYMBOL(rb_replace_node);
